@sk8metal/michi-cli 0.0.7 → 0.0.9
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 +55 -0
- package/README.md +205 -11
- package/dist/scripts/__tests__/create-project.test.d.ts +2 -0
- package/dist/scripts/__tests__/create-project.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/create-project.test.js +247 -0
- package/dist/scripts/__tests__/create-project.test.js.map +1 -0
- package/dist/scripts/__tests__/multi-project-estimate.test.d.ts +2 -0
- package/dist/scripts/__tests__/multi-project-estimate.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/multi-project-estimate.test.js +119 -0
- package/dist/scripts/__tests__/multi-project-estimate.test.js.map +1 -0
- package/dist/scripts/__tests__/setup-existing-project.test.d.ts +2 -0
- package/dist/scripts/__tests__/setup-existing-project.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/setup-existing-project.test.js +129 -0
- package/dist/scripts/__tests__/setup-existing-project.test.js.map +1 -0
- package/dist/scripts/__tests__/setup-interactive.test.d.ts +2 -0
- package/dist/scripts/__tests__/setup-interactive.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/setup-interactive.test.js +160 -0
- package/dist/scripts/__tests__/setup-interactive.test.js.map +1 -0
- package/dist/scripts/config/default-config.json +57 -0
- package/dist/scripts/confluence-sync.d.ts +4 -0
- package/dist/scripts/confluence-sync.d.ts.map +1 -1
- package/dist/scripts/confluence-sync.js +12 -23
- package/dist/scripts/confluence-sync.js.map +1 -1
- package/dist/scripts/constants/__tests__/environments.test.d.ts +2 -0
- package/dist/scripts/constants/__tests__/environments.test.d.ts.map +1 -0
- package/dist/scripts/constants/__tests__/environments.test.js +91 -0
- package/dist/scripts/constants/__tests__/environments.test.js.map +1 -0
- package/dist/scripts/constants/__tests__/languages.test.d.ts +2 -0
- package/dist/scripts/constants/__tests__/languages.test.d.ts.map +1 -0
- package/dist/scripts/constants/__tests__/languages.test.js +82 -0
- package/dist/scripts/constants/__tests__/languages.test.js.map +1 -0
- package/dist/scripts/constants/environments.d.ts +33 -0
- package/dist/scripts/constants/environments.d.ts.map +1 -0
- package/dist/scripts/constants/environments.js +49 -0
- package/dist/scripts/constants/environments.js.map +1 -0
- package/dist/scripts/constants/languages.d.ts +23 -0
- package/dist/scripts/constants/languages.d.ts.map +1 -0
- package/dist/scripts/constants/languages.js +53 -0
- package/dist/scripts/constants/languages.js.map +1 -0
- package/dist/scripts/create-project.d.ts +4 -0
- package/dist/scripts/create-project.d.ts.map +1 -1
- package/dist/scripts/create-project.js +227 -137
- package/dist/scripts/create-project.js.map +1 -1
- package/dist/scripts/jira-sync.d.ts.map +1 -1
- package/dist/scripts/jira-sync.js +15 -0
- package/dist/scripts/jira-sync.js.map +1 -1
- package/dist/scripts/list-projects.d.ts.map +1 -1
- package/dist/scripts/list-projects.js +42 -15
- package/dist/scripts/list-projects.js.map +1 -1
- package/dist/scripts/multi-project-estimate.d.ts.map +1 -1
- package/dist/scripts/multi-project-estimate.js +56 -21
- package/dist/scripts/multi-project-estimate.js.map +1 -1
- package/dist/scripts/resource-dashboard.d.ts.map +1 -1
- package/dist/scripts/resource-dashboard.js +74 -17
- package/dist/scripts/resource-dashboard.js.map +1 -1
- package/dist/scripts/setup-existing-project.d.ts +3 -1
- package/dist/scripts/setup-existing-project.d.ts.map +1 -1
- package/dist/scripts/setup-existing-project.js +306 -217
- package/dist/scripts/setup-existing-project.js.map +1 -1
- package/dist/scripts/setup-interactive.d.ts +10 -0
- package/dist/scripts/setup-interactive.d.ts.map +1 -0
- package/dist/scripts/setup-interactive.js +413 -0
- package/dist/scripts/setup-interactive.js.map +1 -0
- package/dist/scripts/template/__tests__/renderer.test.d.ts +2 -0
- package/dist/scripts/template/__tests__/renderer.test.d.ts.map +1 -0
- package/dist/scripts/template/__tests__/renderer.test.js +165 -0
- package/dist/scripts/template/__tests__/renderer.test.js.map +1 -0
- package/dist/scripts/template/renderer.d.ts +70 -0
- package/dist/scripts/template/renderer.d.ts.map +1 -0
- package/dist/scripts/template/renderer.js +99 -0
- package/dist/scripts/template/renderer.js.map +1 -0
- package/dist/scripts/utils/__tests__/config-validator.test.js +5 -0
- package/dist/scripts/utils/__tests__/config-validator.test.js.map +1 -1
- package/dist/scripts/utils/__tests__/spec-updater.test.d.ts +5 -0
- package/dist/scripts/utils/__tests__/spec-updater.test.d.ts.map +1 -0
- package/dist/scripts/utils/__tests__/spec-updater.test.js +158 -0
- package/dist/scripts/utils/__tests__/spec-updater.test.js.map +1 -0
- package/dist/scripts/utils/confluence-hierarchy.d.ts +2 -1
- package/dist/scripts/utils/confluence-hierarchy.d.ts.map +1 -1
- package/dist/scripts/utils/confluence-hierarchy.js +5 -0
- package/dist/scripts/utils/confluence-hierarchy.js.map +1 -1
- package/dist/scripts/utils/project-finder.d.ts +30 -0
- package/dist/scripts/utils/project-finder.d.ts.map +1 -0
- package/dist/scripts/utils/project-finder.js +147 -0
- package/dist/scripts/utils/project-finder.js.map +1 -0
- package/dist/scripts/utils/spec-updater.d.ts +72 -0
- package/dist/scripts/utils/spec-updater.d.ts.map +1 -0
- package/dist/scripts/utils/spec-updater.js +141 -0
- package/dist/scripts/utils/spec-updater.js.map +1 -0
- package/dist/scripts/utils/template-finder.d.ts +37 -0
- package/dist/scripts/utils/template-finder.d.ts.map +1 -0
- package/dist/scripts/utils/template-finder.js +63 -0
- package/dist/scripts/utils/template-finder.js.map +1 -0
- package/dist/src/__tests__/integration/setup/claude-agent.test.d.ts +5 -0
- package/dist/src/__tests__/integration/setup/claude-agent.test.d.ts.map +1 -0
- package/dist/src/__tests__/integration/setup/claude-agent.test.js +125 -0
- package/dist/src/__tests__/integration/setup/claude-agent.test.js.map +1 -0
- package/dist/src/__tests__/integration/setup/claude.test.d.ts +5 -0
- package/dist/src/__tests__/integration/setup/claude.test.d.ts.map +1 -0
- package/dist/src/__tests__/integration/setup/claude.test.js +111 -0
- package/dist/src/__tests__/integration/setup/claude.test.js.map +1 -0
- package/dist/src/__tests__/integration/setup/cursor.test.d.ts +5 -0
- package/dist/src/__tests__/integration/setup/cursor.test.d.ts.map +1 -0
- package/dist/src/__tests__/integration/setup/cursor.test.js +162 -0
- package/dist/src/__tests__/integration/setup/cursor.test.js.map +1 -0
- package/dist/src/__tests__/integration/setup/helpers/fs-assertions.d.ts +32 -0
- package/dist/src/__tests__/integration/setup/helpers/fs-assertions.d.ts.map +1 -0
- package/dist/src/__tests__/integration/setup/helpers/fs-assertions.js +72 -0
- package/dist/src/__tests__/integration/setup/helpers/fs-assertions.js.map +1 -0
- package/dist/src/__tests__/integration/setup/helpers/test-project.d.ts +38 -0
- package/dist/src/__tests__/integration/setup/helpers/test-project.d.ts.map +1 -0
- package/dist/src/__tests__/integration/setup/helpers/test-project.js +83 -0
- package/dist/src/__tests__/integration/setup/helpers/test-project.js.map +1 -0
- package/dist/src/__tests__/integration/setup/validation.test.d.ts +5 -0
- package/dist/src/__tests__/integration/setup/validation.test.d.ts.map +1 -0
- package/dist/src/__tests__/integration/setup/validation.test.js +318 -0
- package/dist/src/__tests__/integration/setup/validation.test.js.map +1 -0
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +20 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/setup-existing.d.ts +22 -0
- package/dist/src/commands/setup-existing.d.ts.map +1 -0
- package/dist/src/commands/setup-existing.js +408 -0
- package/dist/src/commands/setup-existing.js.map +1 -0
- package/dist/vitest.config.d.ts.map +1 -1
- package/dist/vitest.config.js +9 -6
- package/dist/vitest.config.js.map +1 -1
- package/docs/README.md +2 -2
- package/docs/contributing/development.md +37 -0
- package/docs/getting-started/{new-project-setup.md → new-repository-setup.md} +165 -38
- package/docs/getting-started/quick-start.md +57 -6
- package/docs/getting-started/setup.md +551 -180
- package/docs/guides/customization.md +4 -4
- package/docs/guides/multi-project.md +12 -9
- package/docs/reference/quick-reference.md +27 -18
- package/docs/testing/integration-tests.md +297 -0
- package/docs/testing-strategy.md +87 -0
- package/package.json +23 -6
- package/scripts/__tests__/create-project.test.ts +292 -0
- package/scripts/__tests__/multi-project-estimate.test.ts +145 -0
- package/scripts/__tests__/setup-existing-project.test.ts +147 -0
- package/scripts/__tests__/setup-interactive.test.ts +199 -0
- package/scripts/confluence-sync.ts +17 -29
- package/scripts/constants/__tests__/environments.test.ts +110 -0
- package/scripts/constants/__tests__/languages.test.ts +100 -0
- package/scripts/constants/environments.ts +61 -0
- package/scripts/constants/languages.ts +70 -0
- package/scripts/copy-static-assets.js +50 -0
- package/scripts/create-project.ts +251 -158
- package/scripts/jira-sync.ts +16 -1
- package/scripts/list-projects.ts +51 -24
- package/scripts/multi-project-estimate.ts +58 -22
- package/scripts/resource-dashboard.ts +91 -26
- package/scripts/setup-existing-project.ts +350 -230
- package/scripts/setup-existing.sh +159 -25
- package/scripts/setup-interactive.ts +565 -0
- package/scripts/template/__tests__/renderer.test.ts +207 -0
- package/scripts/template/renderer.ts +133 -0
- package/scripts/utils/__tests__/config-validator.test.ts +6 -0
- package/scripts/utils/__tests__/spec-updater.test.ts +220 -0
- package/scripts/utils/confluence-hierarchy.ts +7 -1
- package/scripts/utils/project-finder.ts +184 -0
- package/scripts/utils/spec-updater.ts +212 -0
- package/scripts/utils/template-finder.ts +75 -0
- package/templates/claude/commands/michi/confluence-sync.md +38 -0
- package/templates/claude/commands/michi/project-switch.md +36 -0
- package/templates/claude/rules/atlassian-integration.md +35 -0
- package/templates/claude/rules/michi-core.md +54 -0
- package/templates/claude-agent/README.md +25 -0
- package/templates/cursor/commands/michi/confluence-sync.md +76 -0
- package/templates/cursor/commands/michi/project-switch.md +69 -0
- package/templates/cursor/rules/atlassian-mcp.mdc +188 -0
- package/templates/cursor/rules/github-ssot.mdc +151 -0
- package/templates/cursor/rules/multi-project.mdc +81 -0
- package/scripts/setup-env.sh +0 -52
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* プロジェクト検出ユーティリティ
|
|
3
|
+
* リポジトリルートを検出し、projects/配下のプロジェクトを検索
|
|
4
|
+
* 複数プロジェクトが存在する場合、選択機能を提供
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, readdirSync } from 'fs';
|
|
8
|
+
import { resolve, join, dirname } from 'path';
|
|
9
|
+
import { readFileSync } from 'fs';
|
|
10
|
+
import type { ProjectMetadata } from './project-meta.js';
|
|
11
|
+
|
|
12
|
+
export interface ProjectLocation {
|
|
13
|
+
path: string;
|
|
14
|
+
projectId: string;
|
|
15
|
+
projectName: string;
|
|
16
|
+
jiraProjectKey: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 指定されたディレクトリに.kiro/project.jsonが存在するか確認
|
|
21
|
+
*/
|
|
22
|
+
function hasProjectJson(dir: string): boolean {
|
|
23
|
+
const projectJsonPath = join(dir, '.kiro', 'project.json');
|
|
24
|
+
return existsSync(projectJsonPath);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* プロジェクトメタデータを読み込む
|
|
29
|
+
*/
|
|
30
|
+
function loadProjectMetadata(dir: string): ProjectMetadata | null {
|
|
31
|
+
const projectJsonPath = join(dir, '.kiro', 'project.json');
|
|
32
|
+
|
|
33
|
+
if (!existsSync(projectJsonPath)) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const content = readFileSync(projectJsonPath, 'utf-8');
|
|
39
|
+
const meta = JSON.parse(content) as ProjectMetadata;
|
|
40
|
+
return meta;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
// パースエラーなどは無視
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 現在のディレクトリから親ディレクトリを遡って.kiro/project.jsonを検索
|
|
49
|
+
*/
|
|
50
|
+
export function findCurrentProject(startDir: string = process.cwd()): ProjectLocation | null {
|
|
51
|
+
let currentDir = resolve(startDir);
|
|
52
|
+
const root = resolve('/');
|
|
53
|
+
|
|
54
|
+
while (currentDir !== root && currentDir !== dirname(currentDir)) {
|
|
55
|
+
if (hasProjectJson(currentDir)) {
|
|
56
|
+
const meta = loadProjectMetadata(currentDir);
|
|
57
|
+
if (meta) {
|
|
58
|
+
return {
|
|
59
|
+
path: currentDir,
|
|
60
|
+
projectId: meta.projectId,
|
|
61
|
+
projectName: meta.projectName,
|
|
62
|
+
jiraProjectKey: meta.jiraProjectKey
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const parentDir = dirname(currentDir);
|
|
68
|
+
if (parentDir === currentDir) {
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
currentDir = parentDir;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* リポジトリルートを検出
|
|
79
|
+
* .gitディレクトリまたはprojects/ディレクトリの存在から判断
|
|
80
|
+
*/
|
|
81
|
+
export function findRepositoryRoot(startDir: string = process.cwd()): string {
|
|
82
|
+
let currentDir = resolve(startDir);
|
|
83
|
+
const root = resolve('/');
|
|
84
|
+
|
|
85
|
+
while (currentDir !== root && currentDir !== dirname(currentDir)) {
|
|
86
|
+
// .gitディレクトリまたはprojects/ディレクトリが存在する場合、リポジトリルートと判断
|
|
87
|
+
if (existsSync(join(currentDir, '.git')) || existsSync(join(currentDir, 'projects'))) {
|
|
88
|
+
return currentDir;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const parentDir = dirname(currentDir);
|
|
92
|
+
if (parentDir === currentDir) {
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
currentDir = parentDir;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// リポジトリルートが見つからない場合、現在のディレクトリを返す
|
|
99
|
+
return resolve(startDir);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* リポジトリルートからprojects/配下の全プロジェクトを検索
|
|
104
|
+
* 統一されたディレクトリ構成: すべてのプロジェクトはprojects/{project-id}/配下に配置
|
|
105
|
+
*/
|
|
106
|
+
export function findAllProjects(
|
|
107
|
+
searchDir?: string
|
|
108
|
+
): ProjectLocation[] {
|
|
109
|
+
const projects: ProjectLocation[] = [];
|
|
110
|
+
|
|
111
|
+
// リポジトリルートを検出
|
|
112
|
+
const repoRoot = searchDir ? findRepositoryRoot(searchDir) : findRepositoryRoot();
|
|
113
|
+
const projectsDir = join(repoRoot, 'projects');
|
|
114
|
+
|
|
115
|
+
// projects/ディレクトリが存在しない場合は空配列を返す
|
|
116
|
+
if (!existsSync(projectsDir)) {
|
|
117
|
+
return projects;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
// projects/配下のディレクトリを取得
|
|
122
|
+
const entries = readdirSync(projectsDir, { withFileTypes: true });
|
|
123
|
+
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
// 隠しディレクトリはスキップ
|
|
126
|
+
if (entry.name.startsWith('.')) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (entry.isDirectory()) {
|
|
131
|
+
const projectDir = join(projectsDir, entry.name);
|
|
132
|
+
// プロジェクトディレクトリに.kiro/project.jsonがあるか確認
|
|
133
|
+
if (hasProjectJson(projectDir)) {
|
|
134
|
+
const meta = loadProjectMetadata(projectDir);
|
|
135
|
+
if (meta) {
|
|
136
|
+
projects.push({
|
|
137
|
+
path: projectDir,
|
|
138
|
+
projectId: meta.projectId,
|
|
139
|
+
projectName: meta.projectName,
|
|
140
|
+
jiraProjectKey: meta.jiraProjectKey
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
// アクセス権限エラーなどは無視
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return projects;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 複数プロジェクトから選択する(対話的)
|
|
155
|
+
*/
|
|
156
|
+
export async function selectProject(
|
|
157
|
+
projects: ProjectLocation[],
|
|
158
|
+
question: (prompt: string) => Promise<string>
|
|
159
|
+
): Promise<ProjectLocation | null> {
|
|
160
|
+
if (projects.length === 0) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (projects.length === 1) {
|
|
165
|
+
return projects[0];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
console.log('\n📋 複数のプロジェクトが見つかりました:');
|
|
169
|
+
projects.forEach((project, index) => {
|
|
170
|
+
console.log(` ${index + 1}. ${project.projectName} (${project.projectId}) - ${project.path}`);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const answer = await question(`\n選択してください [1-${projects.length}]: `);
|
|
174
|
+
const index = parseInt(answer.trim(), 10) - 1;
|
|
175
|
+
|
|
176
|
+
if (index >= 0 && index < projects.length) {
|
|
177
|
+
return projects[index];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 無効な入力の場合は最初のプロジェクトを返す
|
|
181
|
+
console.log('⚠️ 無効な選択です。最初のプロジェクトを使用します。');
|
|
182
|
+
return projects[0];
|
|
183
|
+
}
|
|
184
|
+
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spec.json 更新ユーティリティ
|
|
3
|
+
* Confluence/JIRA 同期後に spec.json を更新する
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
7
|
+
import { resolve, dirname } from 'path';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* spec.json の型定義
|
|
11
|
+
*/
|
|
12
|
+
export interface SpecJson {
|
|
13
|
+
featureName?: string;
|
|
14
|
+
projectName?: string;
|
|
15
|
+
confluence?: {
|
|
16
|
+
spaceKey?: string;
|
|
17
|
+
requirements?: {
|
|
18
|
+
pageId?: string;
|
|
19
|
+
url?: string;
|
|
20
|
+
title?: string;
|
|
21
|
+
};
|
|
22
|
+
design?: {
|
|
23
|
+
pageId?: string;
|
|
24
|
+
url?: string;
|
|
25
|
+
title?: string;
|
|
26
|
+
};
|
|
27
|
+
tasks?: {
|
|
28
|
+
pageId?: string;
|
|
29
|
+
url?: string;
|
|
30
|
+
title?: string;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
jira?: {
|
|
34
|
+
projectKey?: string;
|
|
35
|
+
epicKey?: string;
|
|
36
|
+
epicUrl?: string;
|
|
37
|
+
storyKeys?: string[];
|
|
38
|
+
};
|
|
39
|
+
milestones?: {
|
|
40
|
+
requirementsCompleted?: boolean;
|
|
41
|
+
designCompleted?: boolean;
|
|
42
|
+
tasksCompleted?: boolean;
|
|
43
|
+
jiraSyncCompleted?: boolean;
|
|
44
|
+
};
|
|
45
|
+
lastUpdated?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* spec.json を読み込む
|
|
50
|
+
*/
|
|
51
|
+
export function loadSpecJson(featureName: string, projectRoot: string = process.cwd()): SpecJson {
|
|
52
|
+
const specPath = resolve(projectRoot, `.kiro/specs/${featureName}/spec.json`);
|
|
53
|
+
|
|
54
|
+
if (!existsSync(specPath)) {
|
|
55
|
+
// 新規作成する場合は最低限の構造を返す
|
|
56
|
+
return {
|
|
57
|
+
featureName,
|
|
58
|
+
confluence: {},
|
|
59
|
+
jira: {},
|
|
60
|
+
milestones: {},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const content = readFileSync(specPath, 'utf-8');
|
|
66
|
+
return JSON.parse(content);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.warn(`⚠️ Failed to load spec.json from ${specPath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
69
|
+
return {
|
|
70
|
+
featureName,
|
|
71
|
+
confluence: {},
|
|
72
|
+
jira: {},
|
|
73
|
+
milestones: {},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* spec.json を保存する
|
|
80
|
+
* @param featureName 機能名
|
|
81
|
+
* @param spec 保存する spec オブジェクト(lastUpdated フィールドが更新されます)
|
|
82
|
+
* @param projectRoot プロジェクトルート(デフォルト: process.cwd())
|
|
83
|
+
*/
|
|
84
|
+
export function saveSpecJson(featureName: string, spec: SpecJson, projectRoot: string = process.cwd()): void {
|
|
85
|
+
const specDir = resolve(projectRoot, `.kiro/specs/${featureName}`);
|
|
86
|
+
const specPath = resolve(specDir, 'spec.json');
|
|
87
|
+
|
|
88
|
+
// ディレクトリが存在しない場合は作成
|
|
89
|
+
if (!existsSync(specDir)) {
|
|
90
|
+
mkdirSync(specDir, { recursive: true });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// lastUpdated を更新
|
|
94
|
+
spec.lastUpdated = new Date().toISOString();
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
writeFileSync(specPath, JSON.stringify(spec, null, 2), 'utf-8');
|
|
98
|
+
console.log(`✅ Updated spec.json: ${specPath}`);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error(`❌ Failed to save spec.json to ${specPath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Confluence 同期後に spec.json を更新
|
|
107
|
+
*/
|
|
108
|
+
export function updateSpecJsonAfterConfluenceSync(
|
|
109
|
+
featureName: string,
|
|
110
|
+
docType: 'requirements' | 'design' | 'tasks',
|
|
111
|
+
pageInfo: {
|
|
112
|
+
pageId: string;
|
|
113
|
+
url: string;
|
|
114
|
+
title: string;
|
|
115
|
+
spaceKey: string;
|
|
116
|
+
},
|
|
117
|
+
projectRoot: string = process.cwd()
|
|
118
|
+
): void {
|
|
119
|
+
const spec = loadSpecJson(featureName, projectRoot);
|
|
120
|
+
|
|
121
|
+
// Confluence 情報を更新
|
|
122
|
+
if (!spec.confluence) {
|
|
123
|
+
spec.confluence = {};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// スペースキーを設定
|
|
127
|
+
if (pageInfo.spaceKey) {
|
|
128
|
+
spec.confluence.spaceKey = pageInfo.spaceKey;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 新形式:ドキュメントタイプごとのページ情報を更新
|
|
132
|
+
spec.confluence[docType] = {
|
|
133
|
+
pageId: pageInfo.pageId,
|
|
134
|
+
url: pageInfo.url,
|
|
135
|
+
title: pageInfo.title,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// 旧形式(後方互換性のため併記)
|
|
139
|
+
if (docType === 'requirements') {
|
|
140
|
+
(spec.confluence as any).requirementsPageId = pageInfo.pageId;
|
|
141
|
+
(spec.confluence as any).requirementsUrl = pageInfo.url;
|
|
142
|
+
} else if (docType === 'design') {
|
|
143
|
+
(spec.confluence as any).designPageId = pageInfo.pageId;
|
|
144
|
+
(spec.confluence as any).designUrl = pageInfo.url;
|
|
145
|
+
} else if (docType === 'tasks') {
|
|
146
|
+
(spec.confluence as any).tasksPageId = pageInfo.pageId;
|
|
147
|
+
(spec.confluence as any).tasksUrl = pageInfo.url;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// マイルストーンを更新
|
|
151
|
+
if (!spec.milestones) {
|
|
152
|
+
spec.milestones = {};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 新形式:フラットなフィールド
|
|
156
|
+
if (docType === 'requirements') {
|
|
157
|
+
spec.milestones.requirementsCompleted = true;
|
|
158
|
+
// 旧形式(後方互換性のため併記)
|
|
159
|
+
if (!(spec.milestones as any).requirements) {
|
|
160
|
+
(spec.milestones as any).requirements = {};
|
|
161
|
+
}
|
|
162
|
+
(spec.milestones as any).requirements.completed = true;
|
|
163
|
+
} else if (docType === 'design') {
|
|
164
|
+
spec.milestones.designCompleted = true;
|
|
165
|
+
// 旧形式(後方互換性のため併記)
|
|
166
|
+
if (!(spec.milestones as any).design) {
|
|
167
|
+
(spec.milestones as any).design = {};
|
|
168
|
+
}
|
|
169
|
+
(spec.milestones as any).design.completed = true;
|
|
170
|
+
} else if (docType === 'tasks') {
|
|
171
|
+
spec.milestones.tasksCompleted = true;
|
|
172
|
+
// 旧形式(後方互換性のため併記)
|
|
173
|
+
if (!(spec.milestones as any).tasks) {
|
|
174
|
+
(spec.milestones as any).tasks = {};
|
|
175
|
+
}
|
|
176
|
+
(spec.milestones as any).tasks.completed = true;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
saveSpecJson(featureName, spec, projectRoot);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* JIRA 同期後に spec.json を更新
|
|
184
|
+
*/
|
|
185
|
+
export function updateSpecJsonAfterJiraSync(
|
|
186
|
+
featureName: string,
|
|
187
|
+
jiraInfo: {
|
|
188
|
+
projectKey: string;
|
|
189
|
+
epicKey: string;
|
|
190
|
+
epicUrl: string;
|
|
191
|
+
storyKeys: string[];
|
|
192
|
+
},
|
|
193
|
+
projectRoot: string = process.cwd()
|
|
194
|
+
): void {
|
|
195
|
+
const spec = loadSpecJson(featureName, projectRoot);
|
|
196
|
+
|
|
197
|
+
// JIRA 情報を更新
|
|
198
|
+
spec.jira = {
|
|
199
|
+
projectKey: jiraInfo.projectKey,
|
|
200
|
+
epicKey: jiraInfo.epicKey,
|
|
201
|
+
epicUrl: jiraInfo.epicUrl,
|
|
202
|
+
storyKeys: jiraInfo.storyKeys,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// マイルストーンを更新
|
|
206
|
+
if (!spec.milestones) {
|
|
207
|
+
spec.milestones = {};
|
|
208
|
+
}
|
|
209
|
+
spec.milestones.jiraSyncCompleted = true;
|
|
210
|
+
|
|
211
|
+
saveSpecJson(featureName, spec, projectRoot);
|
|
212
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* テンプレートファイル検索ユーティリティ
|
|
3
|
+
*
|
|
4
|
+
* Issue #35: cc-sdd準拠のテンプレート検索
|
|
5
|
+
* Cursor/Claude両環境のテンプレートを優先順位付きで検索
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* templates/ディレクトリからファイルを検索
|
|
13
|
+
*
|
|
14
|
+
* @param michiPath - Michiリポジトリのルートパス
|
|
15
|
+
* @param relativePath - templates/配下の相対パス(例: 'rules/github-ssot.mdc')
|
|
16
|
+
* @returns 見つかったファイルの絶対パス、見つからない場合はnull
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const path = findTemplateFile('/path/to/michi', 'rules/github-ssot.mdc');
|
|
21
|
+
* // returns: '/path/to/michi/templates/cursor/rules/github-ssot.mdc'
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function findTemplateFile(michiPath: string, relativePath: string): string | null {
|
|
25
|
+
// 優先順位1: templates/cursor/
|
|
26
|
+
const cursorPath = join(michiPath, 'templates/cursor', relativePath);
|
|
27
|
+
if (existsSync(cursorPath)) {
|
|
28
|
+
return cursorPath;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 優先順位2: templates/claude/
|
|
32
|
+
const claudePath = join(michiPath, 'templates/claude', relativePath);
|
|
33
|
+
if (existsSync(claudePath)) {
|
|
34
|
+
return claudePath;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 必須テンプレートファイルのバリデーション
|
|
42
|
+
*
|
|
43
|
+
* @param michiPath - Michiリポジトリのルートパス
|
|
44
|
+
* @param requiredFiles - 必須ファイルのリスト
|
|
45
|
+
* @throws Error - 必須ファイルが見つからない場合
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* validateRequiredTemplates('/path/to/michi', [
|
|
50
|
+
* 'rules/github-ssot.mdc',
|
|
51
|
+
* 'commands/michi/confluence-sync.md'
|
|
52
|
+
* ]);
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function validateRequiredTemplates(
|
|
56
|
+
michiPath: string,
|
|
57
|
+
requiredFiles: string[]
|
|
58
|
+
): void {
|
|
59
|
+
const missingFiles: string[] = [];
|
|
60
|
+
|
|
61
|
+
for (const file of requiredFiles) {
|
|
62
|
+
const path = findTemplateFile(michiPath, file);
|
|
63
|
+
if (!path) {
|
|
64
|
+
missingFiles.push(file);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (missingFiles.length > 0) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Missing required template files:\n - ${missingFiles.join('\n - ')}\n\n` +
|
|
71
|
+
`Please check that Michi templates are properly installed at: ${michiPath}/templates/`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: /michi:confluence-sync
|
|
3
|
+
description: Sync specifications to Confluence
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Confluence Sync Command
|
|
7
|
+
|
|
8
|
+
**Important**: Generate output in language specified in {{KIRO_DIR}}/project.json.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
/michi:confluence-sync <feature_name>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Parameters**:
|
|
17
|
+
- `feature_name`: Feature name (e.g., user-auth, payment-api)
|
|
18
|
+
|
|
19
|
+
## Execution Steps
|
|
20
|
+
|
|
21
|
+
1. Read specifications from {{KIRO_DIR}}/specs/{{FEATURE_NAME}}/
|
|
22
|
+
2. Load project metadata from {{KIRO_DIR}}/project.json
|
|
23
|
+
3. Sync requirements.md to Confluence
|
|
24
|
+
4. Sync design.md to Confluence
|
|
25
|
+
5. Create/update Confluence pages with proper labels
|
|
26
|
+
6. Link pages to JIRA Epic (if exists)
|
|
27
|
+
|
|
28
|
+
## Language Handling
|
|
29
|
+
|
|
30
|
+
- Read language from {{KIRO_DIR}}/project.json
|
|
31
|
+
- Generate all output in the specified language
|
|
32
|
+
- Use Confluence labels from project.json for page organization
|
|
33
|
+
|
|
34
|
+
## Project Metadata
|
|
35
|
+
|
|
36
|
+
- Project ID: {{PROJECT_ID}}
|
|
37
|
+
- Kiro directory: {{KIRO_DIR}}
|
|
38
|
+
- Agent directory: {{AGENT_DIR}}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: /michi:project-switch
|
|
3
|
+
description: Switch between projects
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Project Switch Command
|
|
7
|
+
|
|
8
|
+
**Important**: Generate output in language specified in {{KIRO_DIR}}/project.json.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
/michi:project-switch <project_id>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Parameters**:
|
|
17
|
+
- `project_id`: Project ID (e.g., customer-a-service-1, michi)
|
|
18
|
+
|
|
19
|
+
**Examples**:
|
|
20
|
+
```
|
|
21
|
+
/michi:project-switch michi
|
|
22
|
+
/michi:project-switch customer-a-service-1
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Execution Steps
|
|
26
|
+
|
|
27
|
+
1. Identify GitHub repository corresponding to project ID
|
|
28
|
+
2. Clone locally (if not cloned) or checkout
|
|
29
|
+
3. Load and display {{KIRO_DIR}}/project.json
|
|
30
|
+
4. Display corresponding Confluence project page URL
|
|
31
|
+
|
|
32
|
+
## Language Handling
|
|
33
|
+
|
|
34
|
+
- Read language from {{KIRO_DIR}}/project.json
|
|
35
|
+
- Generate all output in the specified language
|
|
36
|
+
- Default to English if language field is missing
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Atlassian Integration
|
|
3
|
+
description: Integration rules for Confluence and JIRA via MCP
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Atlassian Integration
|
|
7
|
+
|
|
8
|
+
## Development Guidelines
|
|
9
|
+
{{DEV_GUIDELINES}}
|
|
10
|
+
|
|
11
|
+
## Language
|
|
12
|
+
All generated documents should be in: **{{LANG_CODE}}**
|
|
13
|
+
|
|
14
|
+
## MCP Integration
|
|
15
|
+
|
|
16
|
+
### Confluence Sync
|
|
17
|
+
- Sync specifications from {{KIRO_DIR}}/specs/ to Confluence
|
|
18
|
+
- Use MCP server for Confluence operations
|
|
19
|
+
- Project labels: Check {{KIRO_DIR}}/project.json
|
|
20
|
+
|
|
21
|
+
### JIRA Sync
|
|
22
|
+
- Create Epic and Stories from {{KIRO_DIR}}/specs/{{FEATURE_NAME}}/tasks.md
|
|
23
|
+
- Use JIRA project key: {{PROJECT_ID}}
|
|
24
|
+
- Link Confluence pages automatically
|
|
25
|
+
|
|
26
|
+
## Project Metadata
|
|
27
|
+
- Project ID: {{PROJECT_ID}}
|
|
28
|
+
- Kiro directory: {{KIRO_DIR}}
|
|
29
|
+
- Agent directory: {{AGENT_DIR}}
|
|
30
|
+
|
|
31
|
+
## Workflow
|
|
32
|
+
1. Create spec in {{KIRO_DIR}}/specs/{{FEATURE_NAME}}/
|
|
33
|
+
2. Sync to Confluence via MCP
|
|
34
|
+
3. Create JIRA Epic/Stories
|
|
35
|
+
4. Link Confluence pages to JIRA
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Michi Core Principles
|
|
3
|
+
description: Core principles for GitHub SSoT and multi-project management
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Michi Core Principles
|
|
7
|
+
|
|
8
|
+
## Development Guidelines
|
|
9
|
+
{{DEV_GUIDELINES}}
|
|
10
|
+
|
|
11
|
+
## Language
|
|
12
|
+
All generated documents should be in: **{{LANG_CODE}}**
|
|
13
|
+
|
|
14
|
+
Reference the language field in {{KIRO_DIR}}/project.json.
|
|
15
|
+
|
|
16
|
+
## Single Source of Truth (SSoT)
|
|
17
|
+
|
|
18
|
+
### GitHub as SSoT
|
|
19
|
+
- **All specifications are managed in GitHub** ({{KIRO_DIR}}/specs/)
|
|
20
|
+
- Confluence is **reference and approval only** (editing is GitHub only)
|
|
21
|
+
- Avoid duplicate management
|
|
22
|
+
|
|
23
|
+
### Data Flow
|
|
24
|
+
```
|
|
25
|
+
GitHub ({{KIRO_DIR}}/specs/) ← Source of truth (editable)
|
|
26
|
+
↓ sync
|
|
27
|
+
Confluence ← Display and approval (read-only)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Multi-Project Management
|
|
31
|
+
|
|
32
|
+
### Project Identification
|
|
33
|
+
- All operations reference {{KIRO_DIR}}/project.json
|
|
34
|
+
- Dynamically use project ID, JIRA key, Confluence labels
|
|
35
|
+
- Project ID: {{PROJECT_ID}}
|
|
36
|
+
|
|
37
|
+
### Naming Conventions
|
|
38
|
+
|
|
39
|
+
#### Confluence Pages
|
|
40
|
+
- Format: `[{projectName}] {document_type}`
|
|
41
|
+
- Example: `[{{PROJECT_ID}}] Requirements`
|
|
42
|
+
|
|
43
|
+
#### JIRA Epic/Story
|
|
44
|
+
- Format: `[{JIRA_KEY}] {title}`
|
|
45
|
+
- Use project metadata from {{KIRO_DIR}}/project.json
|
|
46
|
+
|
|
47
|
+
## Agent Directory
|
|
48
|
+
- Agent configuration: {{AGENT_DIR}}
|
|
49
|
+
- Rules location: {{AGENT_DIR}}/rules/
|
|
50
|
+
- Commands location: {{AGENT_DIR}}/commands/
|
|
51
|
+
|
|
52
|
+
## Feature Development
|
|
53
|
+
- Feature name: {{FEATURE_NAME}}
|
|
54
|
+
- Spec location: {{KIRO_DIR}}/specs/{{FEATURE_NAME}}/
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Claude Agent Templates
|
|
2
|
+
|
|
3
|
+
This directory contains templates for Claude Agent configuration.
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
claude-agent/
|
|
9
|
+
├── README.md
|
|
10
|
+
└── (additional templates)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
These templates are used when setting up Claude Agent for a project.
|
|
16
|
+
Placeholders are replaced with project-specific values during setup.
|
|
17
|
+
|
|
18
|
+
## Placeholders
|
|
19
|
+
|
|
20
|
+
- {{LANG_CODE}}: Language code (ja, en, etc.)
|
|
21
|
+
- {{DEV_GUIDELINES}}: Development guidelines
|
|
22
|
+
- {{KIRO_DIR}}: Kiro directory (default: .kiro)
|
|
23
|
+
- {{AGENT_DIR}}: Agent directory (default: .claude)
|
|
24
|
+
- {{PROJECT_ID}}: Project ID
|
|
25
|
+
- {{FEATURE_NAME}}: Feature name
|