@sk8metal/michi-cli 0.20.0 → 0.22.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 +129 -0
- package/README.md +1 -1
- package/dist/scripts/multi-project-estimate.js +4 -4
- package/dist/scripts/multi-project-estimate.js.map +1 -1
- package/dist/scripts/phase-runner.js +16 -16
- package/dist/scripts/phase-runner.js.map +1 -1
- package/dist/scripts/pr-automation.js +2 -2
- package/dist/scripts/pr-automation.js.map +1 -1
- package/dist/scripts/pre-flight-check.js +4 -4
- package/dist/scripts/pre-flight-check.js.map +1 -1
- package/dist/scripts/spec-impl-workflow.js +4 -4
- package/dist/scripts/spec-impl-workflow.js.map +1 -1
- package/dist/scripts/template/multi-repo-renderer.js +1 -1
- package/dist/scripts/template/multi-repo-renderer.js.map +1 -1
- package/dist/scripts/template/renderer.d.ts +6 -6
- package/dist/scripts/template/renderer.js +7 -7
- package/dist/scripts/test-execution-generator.js +11 -11
- package/dist/scripts/test-execution-generator.js.map +1 -1
- package/dist/scripts/utils/config-loader.d.ts +1 -1
- package/dist/scripts/utils/config-loader.js +4 -4
- package/dist/scripts/utils/config-loader.js.map +1 -1
- package/dist/scripts/utils/confluence-hierarchy.js +1 -1
- package/dist/scripts/utils/confluence-hierarchy.js.map +1 -1
- package/dist/scripts/utils/docker-requirement-detector.js +3 -3
- package/dist/scripts/utils/docker-requirement-detector.js.map +1 -1
- package/dist/scripts/utils/language-detector.js +2 -2
- package/dist/scripts/utils/language-detector.js.map +1 -1
- package/dist/scripts/utils/multi-repo-validator.d.ts +1 -1
- package/dist/scripts/utils/multi-repo-validator.js +3 -3
- package/dist/scripts/utils/multi-repo-validator.js.map +1 -1
- package/dist/scripts/utils/project-analyzer.d.ts +1 -1
- package/dist/scripts/utils/project-analyzer.js +2 -2
- package/dist/scripts/utils/project-analyzer.js.map +1 -1
- package/dist/scripts/utils/spec-archiver.d.ts +1 -1
- package/dist/scripts/utils/spec-archiver.js +5 -5
- package/dist/scripts/utils/spec-archiver.js.map +1 -1
- package/dist/scripts/utils/spec-updater.js +2 -2
- package/dist/scripts/utils/spec-updater.js.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/template-applier.js +1 -1
- package/dist/scripts/utils/template-applier.js.map +1 -1
- package/dist/scripts/validate-phase.js +4 -4
- package/dist/scripts/validate-phase.js.map +1 -1
- package/dist/src/application/use-cases/spec/archive-spec.js +1 -1
- package/dist/src/application/use-cases/spec/archive-spec.js.map +1 -1
- package/dist/src/commands/migrate.js +14 -14
- package/dist/src/commands/migrate.js.map +1 -1
- package/dist/src/infrastructure/external-apis/atlassian/confluence/hierarchy.js +1 -1
- package/dist/src/infrastructure/external-apis/atlassian/confluence/hierarchy.js.map +1 -1
- package/dist/src/infrastructure/external-apis/atlassian/confluence/sync-service.js +2 -2
- package/dist/src/infrastructure/external-apis/atlassian/confluence/sync-service.js.map +1 -1
- package/dist/src/infrastructure/external-apis/atlassian/jira/issue-builder.js +1 -1
- package/dist/src/infrastructure/external-apis/atlassian/jira/issue-builder.js.map +1 -1
- package/dist/src/infrastructure/external-apis/atlassian/jira/sync-service.js +3 -3
- package/dist/src/infrastructure/external-apis/atlassian/jira/sync-service.js.map +1 -1
- package/dist/src/infrastructure/filesystem/project-meta.d.ts +1 -1
- package/dist/src/infrastructure/filesystem/project-meta.js +2 -2
- package/dist/src/infrastructure/filesystem/project-meta.js.map +1 -1
- package/dist/src/presentation/commands/init/handler.js +1 -1
- package/dist/src/presentation/commands/init/handler.js.map +1 -1
- package/dist/src/presentation/commands/init/register.js +2 -2
- package/dist/src/presentation/commands/init/register.js.map +1 -1
- package/dist/src/presentation/commands/init/setup.d.ts +1 -1
- package/dist/src/presentation/commands/init/setup.js +6 -6
- package/dist/src/presentation/commands/init/setup.js.map +1 -1
- package/dist/src/presentation/commands/init/templates.js +5 -5
- package/dist/src/presentation/commands/init/templates.js.map +1 -1
- package/dist/src/presentation/commands/workflow/orchestrator.js +2 -2
- package/dist/src/presentation/commands/workflow/orchestrator.js.map +1 -1
- package/docs/MIGRATION.md +2 -2
- package/docs/architecture.md +5 -5
- package/docs/getting-started/configuration.md +2 -2
- package/docs/guides/claude-code.md +10 -10
- package/docs/guides/comprehensive-verification-guide.md +29 -29
- package/docs/guides/interactive-config.md +2 -2
- package/docs/guides/workflow.md +25 -25
- package/docs/onion-architecture-phase0-complete.md +1 -1
- package/docs/reference/ai-commands.md +12 -12
- package/docs/reference/cli.md +4 -4
- package/docs/reference/environment-variables.md +1 -1
- package/docs/troubleshooting.md +6 -6
- package/package.json +1 -1
- package/scripts/README.md +2 -3
- package/scripts/__tests__/create-project.test.ts +6 -6
- package/scripts/__tests__/multi-project-estimate.test.ts +1 -1
- package/scripts/build/README.md +1 -1
- package/scripts/multi-project-estimate.ts +4 -4
- package/scripts/phase-runner.ts +16 -16
- package/scripts/pr-automation.ts +2 -2
- package/scripts/pre-flight-check.ts +4 -4
- package/scripts/spec-impl-workflow.ts +4 -4
- package/scripts/template/__tests__/renderer.test.ts +34 -34
- package/scripts/template/multi-repo-renderer.ts +1 -1
- package/scripts/template/renderer.ts +9 -9
- package/scripts/test-execution-generator.ts +11 -11
- package/scripts/utils/__tests__/config-loader.test.ts +14 -9
- package/scripts/utils/__tests__/config-validator.test.ts +8 -8
- package/scripts/utils/__tests__/multi-repo-validator.test.ts +17 -17
- package/scripts/utils/__tests__/project-analyzer.test.ts +28 -28
- package/scripts/utils/__tests__/project-meta.test.ts +22 -22
- package/scripts/utils/__tests__/spec-updater.test.ts +3 -3
- package/scripts/utils/config-loader.ts +6 -6
- package/scripts/utils/confluence-hierarchy.ts +1 -1
- package/scripts/utils/docker-requirement-detector.ts +3 -3
- package/scripts/utils/language-detector.ts +2 -2
- package/scripts/utils/multi-repo-validator.ts +3 -3
- package/scripts/utils/project-analyzer.ts +2 -2
- package/scripts/utils/spec-archiver.ts +5 -5
- package/scripts/utils/spec-updater.ts +2 -2
- package/scripts/utils/tasks-converter.ts +2 -2
- package/scripts/utils/template-applier.ts +1 -1
- package/scripts/validate-phase.ts +4 -4
- package/templates/claude-agent/README.md +1 -1
- package/templates/claude-agent/agents/designer.md +4 -4
- package/templates/claude-agent/agents/developer.md +2 -2
- package/templates/claude-agent/agents/doc-reviewer.md +4 -4
- package/templates/claude-agent/agents/manager-agent.md +3 -3
- package/templates/claude-agent/agents/repo-spec-executor.md +1 -1
- package/templates/claude-agent/agents/tester.md +3 -3
- package/templates/claude-agent/commands/kiro/kiro-spec-impl.md +6 -6
- package/templates/claude-agent/commands/kiro/kiro-spec-tasks.md +10 -10
- package/templates/claude-agent/rules/doc-review.md +1 -1
- package/templates/common/.kiro/project.json.template +0 -21
package/docs/troubleshooting.md
CHANGED
|
@@ -221,9 +221,9 @@
|
|
|
221
221
|
|
|
222
222
|
**症状**:
|
|
223
223
|
```
|
|
224
|
-
❌ requirements.mdが存在しません。先に/
|
|
225
|
-
❌ design.mdが存在しません。先に/
|
|
226
|
-
❌ tasks.mdが存在しません。先に/
|
|
224
|
+
❌ requirements.mdが存在しません。先に/michi:spec-requirements を実行してください
|
|
225
|
+
❌ design.mdが存在しません。先に/michi:spec-design を実行してください
|
|
226
|
+
❌ tasks.mdが存在しません。先に/michi:spec-tasks を実行してください
|
|
227
227
|
```
|
|
228
228
|
|
|
229
229
|
**原因**:
|
|
@@ -240,7 +240,7 @@
|
|
|
240
240
|
2. 各Phaseを順番に実行
|
|
241
241
|
```bash
|
|
242
242
|
# Phase 0.1: 要件定義
|
|
243
|
-
/
|
|
243
|
+
/michi:spec-requirements calculator-app
|
|
244
244
|
|
|
245
245
|
# Phase 0.2: 設計
|
|
246
246
|
/michi:spec-design calculator-app
|
|
@@ -251,7 +251,7 @@
|
|
|
251
251
|
|
|
252
252
|
3. ファイルの存在確認
|
|
253
253
|
```bash
|
|
254
|
-
ls -la .
|
|
254
|
+
ls -la .michi/specs/calculator-app/
|
|
255
255
|
```
|
|
256
256
|
|
|
257
257
|
## tasks.mdフォーマットエラー
|
|
@@ -287,7 +287,7 @@ tasks.mdはMichiワークフロー形式ではなくAI-DLC形式です。
|
|
|
287
287
|
|
|
288
288
|
3. 変換結果を確認
|
|
289
289
|
```bash
|
|
290
|
-
cat .
|
|
290
|
+
cat .michi/specs/calculator-app/tasks.md
|
|
291
291
|
```
|
|
292
292
|
|
|
293
293
|
### フォーマット検証失敗
|
package/package.json
CHANGED
package/scripts/README.md
CHANGED
|
@@ -143,6 +143,5 @@ npm run markdown:convert <input.md> <output.html>
|
|
|
143
143
|
|
|
144
144
|
## 関連ドキュメント
|
|
145
145
|
|
|
146
|
-
- [
|
|
147
|
-
- [
|
|
148
|
-
- [開発ワークフロー](../.kiro/steering/workflow.md)
|
|
146
|
+
- [アーキテクチャガイド](../docs/architecture.md)
|
|
147
|
+
- [開発ワークフロー](../docs/guides/workflow.md)
|
|
@@ -17,7 +17,7 @@ describe('create-project.ts パス問題', () => {
|
|
|
17
17
|
vi.clearAllMocks();
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
it('.claude, .
|
|
20
|
+
it('.claude, .michi, scripts が actualProjectDir にコピーされる', () => {
|
|
21
21
|
const projectDir = '/test/repo';
|
|
22
22
|
const actualProjectDir = '/test/repo/projects/test-project';
|
|
23
23
|
|
|
@@ -36,9 +36,9 @@ describe('create-project.ts パス問題', () => {
|
|
|
36
36
|
'projects/test-project/.claude/commands',
|
|
37
37
|
);
|
|
38
38
|
|
|
39
|
-
// .
|
|
40
|
-
const kiroSteeringPath = join(actualProjectDir, '.
|
|
41
|
-
expect(kiroSteeringPath).toContain('projects/test-project/.
|
|
39
|
+
// .michi/steering のコピー先パスを検証
|
|
40
|
+
const kiroSteeringPath = join(actualProjectDir, '.michi/steering');
|
|
41
|
+
expect(kiroSteeringPath).toContain('projects/test-project/.michi/steering');
|
|
42
42
|
|
|
43
43
|
// scripts のコピー先パスを検証
|
|
44
44
|
const scriptsPath = join(actualProjectDir, 'scripts', 'test.ts');
|
|
@@ -65,8 +65,8 @@ describe('create-project.ts パス問題', () => {
|
|
|
65
65
|
const directories = [
|
|
66
66
|
join(actualProjectDir, '.claude/rules'),
|
|
67
67
|
join(actualProjectDir, '.claude/commands'),
|
|
68
|
-
join(actualProjectDir, '.
|
|
69
|
-
join(actualProjectDir, '.
|
|
68
|
+
join(actualProjectDir, '.michi/steering'),
|
|
69
|
+
join(actualProjectDir, '.michi/settings/templates'),
|
|
70
70
|
join(actualProjectDir, 'scripts/utils'),
|
|
71
71
|
];
|
|
72
72
|
|
|
@@ -132,7 +132,7 @@ describe('multi-project-estimate pagination', () => {
|
|
|
132
132
|
// 実際の呼び出しでは以下のようなパラメータが使われる:
|
|
133
133
|
// { org: 'test-org', per_page: 100 }
|
|
134
134
|
// { owner: 'org', repo: 'name', path: 'projects', per_page: 100 }
|
|
135
|
-
// { owner: 'org', repo: 'name', path: 'projects/x/.
|
|
135
|
+
// { owner: 'org', repo: 'name', path: 'projects/x/.michi/specs', per_page: 100 }
|
|
136
136
|
});
|
|
137
137
|
|
|
138
138
|
it('エラー発生時にスキップして処理を継続する', async () => {
|
package/scripts/build/README.md
CHANGED
|
@@ -207,11 +207,11 @@ async function aggregateEstimates(): Promise<void> {
|
|
|
207
207
|
'type' in projectEntry && projectEntry.type === 'dir' &&
|
|
208
208
|
'name' in projectEntry) {
|
|
209
209
|
try {
|
|
210
|
-
// projects/{project-id}/.
|
|
210
|
+
// projects/{project-id}/.michi/specs/ ディレクトリを取得(pagination対応)
|
|
211
211
|
const specs = await octokit.paginate('GET /repos/{owner}/{repo}/contents/{path}', {
|
|
212
212
|
owner: org,
|
|
213
213
|
repo: repo.name,
|
|
214
|
-
path: `projects/${(projectEntry as { name: string }).name}/.
|
|
214
|
+
path: `projects/${(projectEntry as { name: string }).name}/.michi/specs`,
|
|
215
215
|
per_page: 100
|
|
216
216
|
});
|
|
217
217
|
|
|
@@ -228,7 +228,7 @@ async function aggregateEstimates(): Promise<void> {
|
|
|
228
228
|
const { data: designFile } = await octokit.repos.getContent({
|
|
229
229
|
owner: org,
|
|
230
230
|
repo: repo.name,
|
|
231
|
-
path: `projects/${projectName}/.
|
|
231
|
+
path: `projects/${projectName}/.michi/specs/${specName}/design.md`
|
|
232
232
|
});
|
|
233
233
|
|
|
234
234
|
if ('content' in designFile) {
|
|
@@ -252,7 +252,7 @@ async function aggregateEstimates(): Promise<void> {
|
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
} catch {
|
|
255
|
-
// プロジェクトディレクトリに.
|
|
255
|
+
// プロジェクトディレクトリに.michi/specsがない場合はスキップ
|
|
256
256
|
continue;
|
|
257
257
|
}
|
|
258
258
|
}
|
package/scripts/phase-runner.ts
CHANGED
|
@@ -51,14 +51,14 @@ async function runRequirementsPhase(feature: string): Promise<PhaseRunResult> {
|
|
|
51
51
|
// Step 1: requirements.md存在確認
|
|
52
52
|
const requirementsPath = join(
|
|
53
53
|
process.cwd(),
|
|
54
|
-
'.
|
|
54
|
+
'.michi',
|
|
55
55
|
'specs',
|
|
56
56
|
feature,
|
|
57
57
|
'requirements.md',
|
|
58
58
|
);
|
|
59
59
|
if (!existsSync(requirementsPath)) {
|
|
60
60
|
errors.push(
|
|
61
|
-
'requirements.mdが存在しません。先に/
|
|
61
|
+
'requirements.mdが存在しません。先に/michi:spec-requirements を実行してください',
|
|
62
62
|
);
|
|
63
63
|
return {
|
|
64
64
|
phase: 'requirements',
|
|
@@ -138,14 +138,14 @@ async function runDesignPhase(feature: string): Promise<PhaseRunResult> {
|
|
|
138
138
|
// Step 1: design.md存在確認
|
|
139
139
|
const designPath = join(
|
|
140
140
|
process.cwd(),
|
|
141
|
-
'.
|
|
141
|
+
'.michi',
|
|
142
142
|
'specs',
|
|
143
143
|
feature,
|
|
144
144
|
'design.md',
|
|
145
145
|
);
|
|
146
146
|
if (!existsSync(designPath)) {
|
|
147
147
|
errors.push(
|
|
148
|
-
'design.mdが存在しません。先に/
|
|
148
|
+
'design.mdが存在しません。先に/michi:spec-design を実行してください',
|
|
149
149
|
);
|
|
150
150
|
return {
|
|
151
151
|
phase: 'design',
|
|
@@ -232,7 +232,7 @@ async function checkTasksPrerequisites(
|
|
|
232
232
|
console.log('✅ プリフライトチェック成功');
|
|
233
233
|
|
|
234
234
|
// tasks.md存在確認
|
|
235
|
-
const tasksPath = join(process.cwd(), '.
|
|
235
|
+
const tasksPath = join(process.cwd(), '.michi', 'specs', feature, 'tasks.md');
|
|
236
236
|
if (!existsSync(tasksPath)) {
|
|
237
237
|
errors.push(
|
|
238
238
|
'tasks.mdが存在しません。先に/michi:spec-tasks を実行してください',
|
|
@@ -383,7 +383,7 @@ function displayTasksPhaseSummary(
|
|
|
383
383
|
if (validation.success && jiraCreated) {
|
|
384
384
|
console.log('\n🎉 タスク分割フェーズが完了しました!');
|
|
385
385
|
console.log('📢 開発チームに実装開始を通知してください');
|
|
386
|
-
console.log('🚀 次のステップ: /
|
|
386
|
+
console.log('🚀 次のステップ: /michi:spec-impl <feature>');
|
|
387
387
|
}
|
|
388
388
|
}
|
|
389
389
|
|
|
@@ -396,7 +396,7 @@ async function runTasksPhase(feature: string): Promise<PhaseRunResult> {
|
|
|
396
396
|
|
|
397
397
|
const errors: string[] = [];
|
|
398
398
|
let jiraCreated = false;
|
|
399
|
-
const tasksPath = join(process.cwd(), '.
|
|
399
|
+
const tasksPath = join(process.cwd(), '.michi', 'specs', feature, 'tasks.md');
|
|
400
400
|
|
|
401
401
|
// 前提条件チェック
|
|
402
402
|
const prereqCheck = await checkTasksPrerequisites(feature);
|
|
@@ -705,7 +705,7 @@ async function updateEnvironmentSpecJson(
|
|
|
705
705
|
answers: { language: string; ciTool: string; needsDocker: boolean },
|
|
706
706
|
errors: string[]
|
|
707
707
|
) {
|
|
708
|
-
const specPath = join(process.cwd(), '.
|
|
708
|
+
const specPath = join(process.cwd(), '.michi', 'specs', feature, 'spec.json');
|
|
709
709
|
if (!existsSync(specPath)) {
|
|
710
710
|
return;
|
|
711
711
|
}
|
|
@@ -752,7 +752,7 @@ function displayEnvironmentSummary(
|
|
|
752
752
|
|
|
753
753
|
console.log('\n📖 次のステップ:');
|
|
754
754
|
console.log(' 1. Phase 2: TDD実装へ進む');
|
|
755
|
-
console.log(` /
|
|
755
|
+
console.log(` /michi:spec-impl ${feature}`);
|
|
756
756
|
|
|
757
757
|
console.log('\n' + '='.repeat(60));
|
|
758
758
|
console.log('✅ Phase 1: 環境構築が完了しました');
|
|
@@ -853,7 +853,7 @@ async function runPhaseAPhase(feature: string): Promise<PhaseRunResult> {
|
|
|
853
853
|
function loadPhaseBTestTypes(feature: string): string[] {
|
|
854
854
|
const selectionPath = join(
|
|
855
855
|
process.cwd(),
|
|
856
|
-
'.
|
|
856
|
+
'.michi',
|
|
857
857
|
'specs',
|
|
858
858
|
feature,
|
|
859
859
|
'test-type-selection.json',
|
|
@@ -949,7 +949,7 @@ function displayPhaseBChecklist(
|
|
|
949
949
|
// 生成されたファイルのサマリー
|
|
950
950
|
const testExecutionDir = join(
|
|
951
951
|
process.cwd(),
|
|
952
|
-
'.
|
|
952
|
+
'.michi',
|
|
953
953
|
'specs',
|
|
954
954
|
feature,
|
|
955
955
|
'test-execution',
|
|
@@ -968,28 +968,28 @@ function displayPhaseBChecklist(
|
|
|
968
968
|
|
|
969
969
|
if (phaseBTypes.includes('performance')) {
|
|
970
970
|
console.log(' [ ] 性能テスト実行');
|
|
971
|
-
console.log(` 📁 .
|
|
971
|
+
console.log(` 📁 .michi/specs/${feature}/test-execution/performance/`);
|
|
972
972
|
console.log(' 📖 詳細はディレクトリ内のREADME/計画書を参照');
|
|
973
973
|
}
|
|
974
974
|
|
|
975
975
|
if (phaseBTypes.includes('security')) {
|
|
976
976
|
console.log(' [ ] セキュリティテスト実行');
|
|
977
|
-
console.log(` 📁 .
|
|
977
|
+
console.log(` 📁 .michi/specs/${feature}/test-execution/security/`);
|
|
978
978
|
console.log(' 📖 詳細はディレクトリ内のREADME/計画書を参照');
|
|
979
979
|
}
|
|
980
980
|
|
|
981
981
|
if (phaseBTypes.includes('integration')) {
|
|
982
982
|
console.log(' [ ] 統合テスト実行');
|
|
983
|
-
console.log(` 📁 .
|
|
983
|
+
console.log(` 📁 .michi/specs/${feature}/test-execution/integration/`);
|
|
984
984
|
}
|
|
985
985
|
|
|
986
986
|
if (phaseBTypes.includes('e2e')) {
|
|
987
987
|
console.log(' [ ] E2Eテスト実行');
|
|
988
|
-
console.log(` 📁 .
|
|
988
|
+
console.log(` 📁 .michi/specs/${feature}/test-execution/e2e/`);
|
|
989
989
|
}
|
|
990
990
|
|
|
991
991
|
console.log('\n参考ドキュメント:');
|
|
992
|
-
console.log(` - .
|
|
992
|
+
console.log(` - .michi/specs/${feature}/test-specs/ (テスト仕様書)`);
|
|
993
993
|
console.log(' - docs/user-guide/testing/test-execution-flow.md');
|
|
994
994
|
|
|
995
995
|
console.log('\n次のステップ:');
|
package/scripts/pr-automation.ts
CHANGED
|
@@ -22,13 +22,13 @@ async function createPR(options: PROptions): Promise<void> {
|
|
|
22
22
|
throw new Error('Missing GitHub credentials. Required: GITHUB_TOKEN');
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
// .
|
|
25
|
+
// .michi/project.json から repository 情報を取得
|
|
26
26
|
let repo: string;
|
|
27
27
|
try {
|
|
28
28
|
repo = getRepositoryInfo();
|
|
29
29
|
} catch (error) {
|
|
30
30
|
throw new Error(
|
|
31
|
-
`Failed to get repository info from .
|
|
31
|
+
`Failed to get repository info from .michi/project.json: ${error instanceof Error ? error.message : error}`,
|
|
32
32
|
);
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -76,10 +76,10 @@ function checkProjectJson(): { errors: string[], warnings: string[] } {
|
|
|
76
76
|
const errors: string[] = [];
|
|
77
77
|
const warnings: string[] = [];
|
|
78
78
|
|
|
79
|
-
const projectJsonPath = join(process.cwd(), '.
|
|
79
|
+
const projectJsonPath = join(process.cwd(), '.michi', 'project.json');
|
|
80
80
|
|
|
81
81
|
if (!existsSync(projectJsonPath)) {
|
|
82
|
-
errors.push('❌ .
|
|
82
|
+
errors.push('❌ .michi/project.json が存在しません');
|
|
83
83
|
errors.push(' → このディレクトリはMichiプロジェクトではありません');
|
|
84
84
|
errors.push(' → セットアップ: npm run setup-existing(michi-practice1の場合)');
|
|
85
85
|
errors.push(' → または、Michiプロジェクトのディレクトリに移動してください');
|
|
@@ -190,7 +190,7 @@ async function checkJiraProject(projectKey: string): Promise<{ errors: string[],
|
|
|
190
190
|
errors.push(`❌ JIRAプロジェクトが存在しません: ${projectKey}`);
|
|
191
191
|
errors.push(` → JIRAプロジェクト作成: ${url}/jira/projects/create`);
|
|
192
192
|
errors.push(` → プロジェクト一覧: ${url}/jira/settings/projects`);
|
|
193
|
-
errors.push(' → または、.
|
|
193
|
+
errors.push(' → または、.michi/project.jsonのjiraProjectKeyを修正してください');
|
|
194
194
|
errors.push(` 現在の設定: "${projectKey}" → 実際に存在するキーに変更`);
|
|
195
195
|
} else if (isAxiosError && error.response?.status === 401) {
|
|
196
196
|
errors.push('❌ JIRA認証エラー(.envの認証情報を確認)');
|
|
@@ -250,7 +250,7 @@ export async function runPreFlightCheck(phase: 'confluence' | 'jira' | 'all'): P
|
|
|
250
250
|
// 4. JIRAプロジェクトチェック(API呼び出し)
|
|
251
251
|
if (phase === 'jira' || phase === 'all') {
|
|
252
252
|
console.log('\n📋 Step 4: JIRAプロジェクト存在チェック');
|
|
253
|
-
const projectJsonPath = join(process.cwd(), '.
|
|
253
|
+
const projectJsonPath = join(process.cwd(), '.michi', 'project.json');
|
|
254
254
|
|
|
255
255
|
const readResult = safeReadJsonFile<ProjectMeta>(projectJsonPath);
|
|
256
256
|
if (!readResult.success) {
|
|
@@ -265,13 +265,13 @@ ${jiraLink}
|
|
|
265
265
|
throw new Error('Missing GitHub credentials. Required: GITHUB_TOKEN');
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
-
// .
|
|
268
|
+
// .michi/project.json から repository 情報を取得
|
|
269
269
|
let repo: string;
|
|
270
270
|
try {
|
|
271
271
|
repo = getRepositoryInfo();
|
|
272
272
|
} catch (error) {
|
|
273
273
|
throw new Error(
|
|
274
|
-
`Failed to get repository info from .
|
|
274
|
+
`Failed to get repository info from .michi/project.json: ${error instanceof Error ? error.message : error}`,
|
|
275
275
|
);
|
|
276
276
|
}
|
|
277
277
|
|
|
@@ -408,13 +408,13 @@ export async function onSpecImplEnd(
|
|
|
408
408
|
throw new Error('Missing GitHub credentials. Required: GITHUB_TOKEN');
|
|
409
409
|
}
|
|
410
410
|
|
|
411
|
-
// .
|
|
411
|
+
// .michi/project.json から repository 情報を取得
|
|
412
412
|
let repo: string;
|
|
413
413
|
try {
|
|
414
414
|
repo = getRepositoryInfo();
|
|
415
415
|
} catch (error) {
|
|
416
416
|
throw new Error(
|
|
417
|
-
`Failed to get repository info from .
|
|
417
|
+
`Failed to get repository info from .michi/project.json: ${error instanceof Error ? error.message : error}`,
|
|
418
418
|
);
|
|
419
419
|
}
|
|
420
420
|
|
|
@@ -9,22 +9,22 @@ import {
|
|
|
9
9
|
describe('renderer', () => {
|
|
10
10
|
describe('createTemplateContext', () => {
|
|
11
11
|
it('should create context with all required fields', () => {
|
|
12
|
-
const context = createTemplateContext('ja', '.
|
|
12
|
+
const context = createTemplateContext('ja', '.michi', '.claude');
|
|
13
13
|
|
|
14
14
|
expect(context).toHaveProperty('LANG_CODE');
|
|
15
15
|
expect(context).toHaveProperty('DEV_GUIDELINES');
|
|
16
|
-
expect(context).toHaveProperty('
|
|
16
|
+
expect(context).toHaveProperty('SPEC_DIR');
|
|
17
17
|
expect(context).toHaveProperty('AGENT_DIR');
|
|
18
18
|
expect(context.LANG_CODE).toBe('ja');
|
|
19
|
-
expect(context.
|
|
19
|
+
expect(context.SPEC_DIR).toBe('.michi');
|
|
20
20
|
expect(context.AGENT_DIR).toBe('.claude');
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
it('should include language-specific guidelines', () => {
|
|
24
|
-
const contextJa = createTemplateContext('ja', '.
|
|
24
|
+
const contextJa = createTemplateContext('ja', '.michi', '.claude');
|
|
25
25
|
expect(contextJa.DEV_GUIDELINES).toContain('日本語');
|
|
26
26
|
|
|
27
|
-
const contextEn = createTemplateContext('en', '.
|
|
27
|
+
const contextEn = createTemplateContext('en', '.michi', '.claude');
|
|
28
28
|
expect(contextEn.DEV_GUIDELINES).toContain('English');
|
|
29
29
|
});
|
|
30
30
|
});
|
|
@@ -32,23 +32,23 @@ describe('renderer', () => {
|
|
|
32
32
|
describe('renderTemplate', () => {
|
|
33
33
|
it('should replace single placeholder', () => {
|
|
34
34
|
const template = 'Language: {{LANG_CODE}}';
|
|
35
|
-
const context = createTemplateContext('ja', '.
|
|
35
|
+
const context = createTemplateContext('ja', '.michi', '.claude');
|
|
36
36
|
const result = renderTemplate(template, context);
|
|
37
37
|
|
|
38
38
|
expect(result).toBe('Language: ja');
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
it('should replace multiple placeholders', () => {
|
|
42
|
-
const template = 'Lang: {{LANG_CODE}}, Kiro: {{
|
|
43
|
-
const context = createTemplateContext('en', '.
|
|
42
|
+
const template = 'Lang: {{LANG_CODE}}, Kiro: {{SPEC_DIR}}, Agent: {{AGENT_DIR}}';
|
|
43
|
+
const context = createTemplateContext('en', '.michi', '.claude');
|
|
44
44
|
const result = renderTemplate(template, context);
|
|
45
45
|
|
|
46
|
-
expect(result).toBe('Lang: en, Kiro: .
|
|
46
|
+
expect(result).toBe('Lang: en, Kiro: .michi, Agent: .claude');
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
it('should replace same placeholder multiple times', () => {
|
|
50
50
|
const template = '{{LANG_CODE}} is {{LANG_CODE}}';
|
|
51
|
-
const context = createTemplateContext('ja', '.
|
|
51
|
+
const context = createTemplateContext('ja', '.michi', '.claude');
|
|
52
52
|
const result = renderTemplate(template, context);
|
|
53
53
|
|
|
54
54
|
expect(result).toBe('ja is ja');
|
|
@@ -56,7 +56,7 @@ describe('renderer', () => {
|
|
|
56
56
|
|
|
57
57
|
it('should leave unknown placeholders unchanged', () => {
|
|
58
58
|
const template = 'Known: {{LANG_CODE}}, Unknown: {{UNKNOWN}}';
|
|
59
|
-
const context = createTemplateContext('ja', '.
|
|
59
|
+
const context = createTemplateContext('ja', '.michi', '.claude');
|
|
60
60
|
const result = renderTemplate(template, context);
|
|
61
61
|
|
|
62
62
|
expect(result).toBe('Known: ja, Unknown: {{UNKNOWN}}');
|
|
@@ -64,7 +64,7 @@ describe('renderer', () => {
|
|
|
64
64
|
|
|
65
65
|
it('should handle DEV_GUIDELINES placeholder', () => {
|
|
66
66
|
const template = '{{DEV_GUIDELINES}}';
|
|
67
|
-
const context = createTemplateContext('ja', '.
|
|
67
|
+
const context = createTemplateContext('ja', '.michi', '.claude');
|
|
68
68
|
const result = renderTemplate(template, context);
|
|
69
69
|
|
|
70
70
|
expect(result).toContain('Think in English');
|
|
@@ -73,7 +73,7 @@ describe('renderer', () => {
|
|
|
73
73
|
|
|
74
74
|
it('should handle empty template', () => {
|
|
75
75
|
const template = '';
|
|
76
|
-
const context = createTemplateContext('ja', '.
|
|
76
|
+
const context = createTemplateContext('ja', '.michi', '.claude');
|
|
77
77
|
const result = renderTemplate(template, context);
|
|
78
78
|
|
|
79
79
|
expect(result).toBe('');
|
|
@@ -81,7 +81,7 @@ describe('renderer', () => {
|
|
|
81
81
|
|
|
82
82
|
it('should handle template with no placeholders', () => {
|
|
83
83
|
const template = 'No placeholders here';
|
|
84
|
-
const context = createTemplateContext('ja', '.
|
|
84
|
+
const context = createTemplateContext('ja', '.michi', '.claude');
|
|
85
85
|
const result = renderTemplate(template, context);
|
|
86
86
|
|
|
87
87
|
expect(result).toBe('No placeholders here');
|
|
@@ -89,59 +89,59 @@ describe('renderer', () => {
|
|
|
89
89
|
|
|
90
90
|
it('should handle multiline template', () => {
|
|
91
91
|
const template = `Line 1: {{LANG_CODE}}
|
|
92
|
-
Line 2: {{
|
|
92
|
+
Line 2: {{SPEC_DIR}}
|
|
93
93
|
Line 3: {{AGENT_DIR}}`;
|
|
94
|
-
const context = createTemplateContext('en', '.
|
|
94
|
+
const context = createTemplateContext('en', '.michi', '.claude');
|
|
95
95
|
const result = renderTemplate(template, context);
|
|
96
96
|
|
|
97
97
|
expect(result).toBe(`Line 1: en
|
|
98
|
-
Line 2: .
|
|
98
|
+
Line 2: .michi
|
|
99
99
|
Line 3: .claude`);
|
|
100
100
|
});
|
|
101
101
|
});
|
|
102
102
|
|
|
103
103
|
describe('renderJsonTemplate', () => {
|
|
104
104
|
it('should render and parse JSON template', () => {
|
|
105
|
-
const template = '{"lang": "{{LANG_CODE}}", "dir": "{{
|
|
106
|
-
const context = createTemplateContext('ja', '.
|
|
105
|
+
const template = '{"lang": "{{LANG_CODE}}", "dir": "{{SPEC_DIR}}"}';
|
|
106
|
+
const context = createTemplateContext('ja', '.michi', '.claude');
|
|
107
107
|
const result = renderJsonTemplate(template, context);
|
|
108
108
|
|
|
109
|
-
expect(result).toEqual({ lang: 'ja', dir: '.
|
|
109
|
+
expect(result).toEqual({ lang: 'ja', dir: '.michi' });
|
|
110
110
|
});
|
|
111
111
|
|
|
112
112
|
it('should handle nested JSON', () => {
|
|
113
|
-
const template = '{"config": {"lang": "{{LANG_CODE}}", "paths": {"kiro": "{{
|
|
114
|
-
const context = createTemplateContext('en', '.
|
|
113
|
+
const template = '{"config": {"lang": "{{LANG_CODE}}", "paths": {"kiro": "{{SPEC_DIR}}"}}}';
|
|
114
|
+
const context = createTemplateContext('en', '.michi', '.claude');
|
|
115
115
|
const result = renderJsonTemplate(template, context);
|
|
116
116
|
|
|
117
117
|
expect(result).toEqual({
|
|
118
118
|
config: {
|
|
119
119
|
lang: 'en',
|
|
120
120
|
paths: {
|
|
121
|
-
kiro: '.
|
|
121
|
+
kiro: '.michi'
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
});
|
|
125
125
|
});
|
|
126
126
|
|
|
127
127
|
it('should handle JSON arrays', () => {
|
|
128
|
-
const template = '["{{LANG_CODE}}", "{{
|
|
129
|
-
const context = createTemplateContext('ja', '.
|
|
128
|
+
const template = '["{{LANG_CODE}}", "{{SPEC_DIR}}", "{{AGENT_DIR}}"]';
|
|
129
|
+
const context = createTemplateContext('ja', '.michi', '.claude');
|
|
130
130
|
const result = renderJsonTemplate(template, context);
|
|
131
131
|
|
|
132
|
-
expect(result).toEqual(['ja', '.
|
|
132
|
+
expect(result).toEqual(['ja', '.michi', '.claude']);
|
|
133
133
|
});
|
|
134
134
|
|
|
135
135
|
it('should throw on invalid JSON', () => {
|
|
136
136
|
const template = 'invalid json {{LANG_CODE}}';
|
|
137
|
-
const context = createTemplateContext('ja', '.
|
|
137
|
+
const context = createTemplateContext('ja', '.michi', '.claude');
|
|
138
138
|
|
|
139
139
|
expect(() => renderJsonTemplate(template, context)).toThrow();
|
|
140
140
|
});
|
|
141
141
|
|
|
142
142
|
it('should throw with descriptive error for invalid JSON', () => {
|
|
143
143
|
const template = '{"incomplete": {{LANG_CODE}}';
|
|
144
|
-
const context = createTemplateContext('ja', '.
|
|
144
|
+
const context = createTemplateContext('ja', '.michi', '.claude');
|
|
145
145
|
|
|
146
146
|
try {
|
|
147
147
|
renderJsonTemplate(template, context);
|
|
@@ -156,13 +156,13 @@ Line 3: .claude`);
|
|
|
156
156
|
expect(errorMessage).toContain('Rendered output');
|
|
157
157
|
expect(errorMessage).toContain('Template context');
|
|
158
158
|
expect(errorMessage).toContain('LANG_CODE=ja');
|
|
159
|
-
expect(errorMessage).toContain('
|
|
159
|
+
expect(errorMessage).toContain('SPEC_DIR=.michi');
|
|
160
160
|
}
|
|
161
161
|
});
|
|
162
162
|
|
|
163
163
|
it('should preserve original error stack', () => {
|
|
164
164
|
const template = '{invalid json}';
|
|
165
|
-
const context = createTemplateContext('en', '.
|
|
165
|
+
const context = createTemplateContext('en', '.michi', '.claude');
|
|
166
166
|
|
|
167
167
|
try {
|
|
168
168
|
renderJsonTemplate(template, context);
|
|
@@ -181,22 +181,22 @@ Line 3: .claude`);
|
|
|
181
181
|
it('should render multiple templates', () => {
|
|
182
182
|
const templates = {
|
|
183
183
|
template1: 'Lang: {{LANG_CODE}}',
|
|
184
|
-
template2: 'Dir: {{
|
|
184
|
+
template2: 'Dir: {{SPEC_DIR}}',
|
|
185
185
|
template3: 'Agent: {{AGENT_DIR}}'
|
|
186
186
|
};
|
|
187
|
-
const context = createTemplateContext('ja', '.
|
|
187
|
+
const context = createTemplateContext('ja', '.michi', '.claude');
|
|
188
188
|
const results = renderTemplates(templates, context);
|
|
189
189
|
|
|
190
190
|
expect(results).toEqual({
|
|
191
191
|
template1: 'Lang: ja',
|
|
192
|
-
template2: 'Dir: .
|
|
192
|
+
template2: 'Dir: .michi',
|
|
193
193
|
template3: 'Agent: .claude'
|
|
194
194
|
});
|
|
195
195
|
});
|
|
196
196
|
|
|
197
197
|
it('should handle empty templates object', () => {
|
|
198
198
|
const templates = {};
|
|
199
|
-
const context = createTemplateContext('ja', '.
|
|
199
|
+
const context = createTemplateContext('ja', '.michi', '.claude');
|
|
200
200
|
const results = renderTemplates(templates, context);
|
|
201
201
|
|
|
202
202
|
expect(results).toEqual({});
|
|
@@ -108,7 +108,7 @@ export const renderMultiRepoTemplate = (
|
|
|
108
108
|
const templateContext: TemplateContext = {
|
|
109
109
|
LANG_CODE: 'ja', // Default language
|
|
110
110
|
DEV_GUIDELINES: '', // Not used for Multi-Repo templates
|
|
111
|
-
|
|
111
|
+
SPEC_DIR: '.michi',
|
|
112
112
|
AGENT_DIR: '.claude',
|
|
113
113
|
PROJECT_NAME: context.PROJECT_NAME,
|
|
114
114
|
JIRA_KEY: context.JIRA_KEY,
|
|
@@ -9,7 +9,7 @@ import { SupportedLanguage, getDevGuidelines } from '../constants/languages.js';
|
|
|
9
9
|
export interface TemplateContext {
|
|
10
10
|
LANG_CODE: SupportedLanguage;
|
|
11
11
|
DEV_GUIDELINES: string;
|
|
12
|
-
|
|
12
|
+
SPEC_DIR: string;
|
|
13
13
|
AGENT_DIR: string;
|
|
14
14
|
PROJECT_ID?: string;
|
|
15
15
|
FEATURE_NAME?: string;
|
|
@@ -23,20 +23,20 @@ export interface TemplateContext {
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Create template context for rendering
|
|
26
|
-
*
|
|
26
|
+
*
|
|
27
27
|
* @param lang - Language code
|
|
28
|
-
* @param
|
|
28
|
+
* @param specDir - .michi directory name (spec directory)
|
|
29
29
|
* @param agentDir - Agent directory name (e.g., .cursor, .claude)
|
|
30
30
|
* @returns Template context object
|
|
31
31
|
*/
|
|
32
32
|
export const createTemplateContext = (
|
|
33
33
|
lang: SupportedLanguage,
|
|
34
|
-
|
|
34
|
+
specDir: string,
|
|
35
35
|
agentDir: string
|
|
36
36
|
): TemplateContext => ({
|
|
37
37
|
LANG_CODE: lang,
|
|
38
38
|
DEV_GUIDELINES: getDevGuidelines(lang),
|
|
39
|
-
|
|
39
|
+
SPEC_DIR: specDir,
|
|
40
40
|
AGENT_DIR: agentDir,
|
|
41
41
|
});
|
|
42
42
|
|
|
@@ -79,10 +79,10 @@ export const renderTemplate = (
|
|
|
79
79
|
*
|
|
80
80
|
* @example
|
|
81
81
|
* ```typescript
|
|
82
|
-
* const template = '{"lang": "{{LANG_CODE}}", "dir": "{{
|
|
83
|
-
* const context = { LANG_CODE: "ja",
|
|
82
|
+
* const template = '{"lang": "{{LANG_CODE}}", "dir": "{{SPEC_DIR}}"}';
|
|
83
|
+
* const context = { LANG_CODE: "ja", SPEC_DIR: ".michi" };
|
|
84
84
|
* const result = renderJsonTemplate(template, context);
|
|
85
|
-
* // Result: { lang: "ja", dir: ".
|
|
85
|
+
* // Result: { lang: "ja", dir: ".michi" }
|
|
86
86
|
* ```
|
|
87
87
|
*/
|
|
88
88
|
export const renderJsonTemplate = <T = unknown>(
|
|
@@ -102,7 +102,7 @@ export const renderJsonTemplate = <T = unknown>(
|
|
|
102
102
|
'Failed to parse rendered JSON template',
|
|
103
103
|
`Original error: ${errorMessage}`,
|
|
104
104
|
`Rendered output (first 500 chars): ${rendered.substring(0, 500)}${rendered.length > 500 ? '...' : ''}`,
|
|
105
|
-
`Template context: LANG_CODE=${context.LANG_CODE},
|
|
105
|
+
`Template context: LANG_CODE=${context.LANG_CODE}, SPEC_DIR=${context.SPEC_DIR}, AGENT_DIR=${context.AGENT_DIR}`
|
|
106
106
|
].join('\n');
|
|
107
107
|
|
|
108
108
|
const detailedError = new Error(debugInfo);
|