@sk8metal/michi-cli 0.0.7 → 0.0.8
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 +22 -0
- package/README.md +3 -2
- 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 +67 -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/create-project.js +198 -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.js +248 -214
- 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/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/vitest.config.d.ts.map +1 -1
- package/dist/vitest.config.js +8 -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} +66 -19
- package/docs/getting-started/setup.md +305 -182
- package/docs/guides/customization.md +1 -1
- package/docs/guides/multi-project.md +11 -8
- package/docs/reference/quick-reference.md +2 -2
- package/docs/testing-strategy.md +87 -0
- package/package.json +17 -5
- 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 +79 -0
- package/scripts/__tests__/setup-interactive.test.ts +199 -0
- package/scripts/confluence-sync.ts +17 -29
- package/scripts/copy-static-assets.js +50 -0
- package/scripts/create-project.ts +219 -156
- 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 +264 -223
- package/scripts/setup-existing.sh +29 -22
- package/scripts/setup-interactive.ts +565 -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
|
@@ -67,6 +67,25 @@ function parseArgs(): ProjectConfig {
|
|
|
67
67
|
return config as ProjectConfig;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* jj/git の依存性をチェックし、利用可能なVCSを返す
|
|
72
|
+
*/
|
|
73
|
+
function checkDependencies(): { vcs: 'jj' | 'git'; version: string } {
|
|
74
|
+
try {
|
|
75
|
+
const jjVersion = execSync('jj --version', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
76
|
+
return { vcs: 'jj', version: jjVersion };
|
|
77
|
+
} catch {
|
|
78
|
+
try {
|
|
79
|
+
const gitVersion = execSync('git --version', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
80
|
+
return { vcs: 'git', version: gitVersion };
|
|
81
|
+
} catch {
|
|
82
|
+
console.error('❌ Neither jj nor git is installed');
|
|
83
|
+
console.error(' Please install jj (https://github.com/martinvonz/jj) or git');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
70
89
|
async function createProject(config: ProjectConfig): Promise<void> {
|
|
71
90
|
const org = config.org || process.env.GITHUB_ORG;
|
|
72
91
|
|
|
@@ -79,10 +98,14 @@ async function createProject(config: ProjectConfig): Promise<void> {
|
|
|
79
98
|
const repoName = config.name;
|
|
80
99
|
const repoUrl = `https://github.com/${org}/${repoName}`;
|
|
81
100
|
const projectDir = resolve(`../${repoName}`);
|
|
82
|
-
|
|
101
|
+
|
|
102
|
+
// VCS依存性チェック
|
|
103
|
+
const deps = checkDependencies();
|
|
104
|
+
|
|
83
105
|
console.log(`🚀 Creating new project: ${config.projectName}`);
|
|
84
106
|
console.log(` Repository: ${org}/${repoName}`);
|
|
85
107
|
console.log(` JIRA: ${config.jiraKey}`);
|
|
108
|
+
console.log(` VCS: ${deps.vcs} (${deps.version})`);
|
|
86
109
|
console.log('');
|
|
87
110
|
|
|
88
111
|
// Step 1: GitHubリポジトリ作成
|
|
@@ -93,16 +116,20 @@ async function createProject(config: ProjectConfig): Promise<void> {
|
|
|
93
116
|
{ stdio: 'inherit' }
|
|
94
117
|
);
|
|
95
118
|
console.log(' ✅ Repository created');
|
|
96
|
-
} catch
|
|
119
|
+
} catch {
|
|
97
120
|
console.log(' ⚠️ Repository may already exist');
|
|
98
121
|
}
|
|
99
122
|
|
|
100
123
|
// Step 2: リポジトリクローン
|
|
101
124
|
console.log('\n📥 Step 2: Cloning repository...');
|
|
102
125
|
try {
|
|
103
|
-
|
|
126
|
+
if (deps.vcs === 'jj') {
|
|
127
|
+
execSync(`jj git clone ${repoUrl} ${projectDir}`, { stdio: 'inherit' });
|
|
128
|
+
} else {
|
|
129
|
+
execSync(`git clone ${repoUrl} ${projectDir}`, { stdio: 'inherit' });
|
|
130
|
+
}
|
|
104
131
|
console.log(' ✅ Repository cloned');
|
|
105
|
-
} catch
|
|
132
|
+
} catch {
|
|
106
133
|
console.log(' ⚠️ Clone failed, checking if directory exists...');
|
|
107
134
|
}
|
|
108
135
|
|
|
@@ -113,175 +140,211 @@ async function createProject(config: ProjectConfig): Promise<void> {
|
|
|
113
140
|
process.exit(1);
|
|
114
141
|
}
|
|
115
142
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// Step 4: cc-sdd導入
|
|
120
|
-
console.log('\n⚙️ Step 4: Installing cc-sdd...');
|
|
121
|
-
execSync('npx cc-sdd@latest --cursor --lang ja --yes', { stdio: 'inherit' });
|
|
122
|
-
console.log(' ✅ cc-sdd installed');
|
|
123
|
-
|
|
124
|
-
// Step 5: .kiro/project.json 作成
|
|
125
|
-
console.log('\n📝 Step 5: Creating project metadata...');
|
|
126
|
-
mkdirSync('.kiro', { recursive: true });
|
|
143
|
+
// 元の作業ディレクトリを保存
|
|
144
|
+
const originalCwd = process.cwd();
|
|
127
145
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
146
|
+
try {
|
|
147
|
+
process.chdir(projectDir);
|
|
148
|
+
console.log(` 📂 Working directory: ${projectDir}`);
|
|
149
|
+
|
|
150
|
+
// Step 4: projects/ディレクトリとプロジェクトディレクトリを作成
|
|
151
|
+
console.log('\n📁 Step 4: Creating project directory structure...');
|
|
152
|
+
const projectsDir = join(projectDir, 'projects');
|
|
153
|
+
const actualProjectDir = join(projectsDir, repoName);
|
|
154
|
+
mkdirSync(actualProjectDir, { recursive: true });
|
|
155
|
+
console.log(` ✅ Project directory created: ${actualProjectDir}`);
|
|
156
|
+
|
|
157
|
+
// Step 5: プロジェクトディレクトリに移動
|
|
158
|
+
process.chdir(actualProjectDir);
|
|
159
|
+
console.log(` 📂 Working directory: ${actualProjectDir}`);
|
|
160
|
+
|
|
161
|
+
// Step 6: cc-sdd導入
|
|
162
|
+
console.log('\n⚙️ Step 6: Installing cc-sdd...');
|
|
163
|
+
execSync('npx cc-sdd@latest --cursor --lang ja --yes', { stdio: 'inherit' });
|
|
164
|
+
console.log(' ✅ cc-sdd installed');
|
|
165
|
+
|
|
166
|
+
// Step 7: .kiro/project.json 作成
|
|
167
|
+
console.log('\n📝 Step 7: Creating project metadata...');
|
|
168
|
+
mkdirSync('.kiro', { recursive: true });
|
|
169
|
+
|
|
170
|
+
// ラベル生成(安全なフォールバック付き)
|
|
171
|
+
const labels = config.labels || (() => {
|
|
172
|
+
// プロジェクトIDからプロジェクトラベル生成
|
|
173
|
+
const projectLabel = repoName.toLowerCase().replace(/[^a-z0-9-]/g, '');
|
|
174
|
+
const labelSet = new Set([`project:${projectLabel}`]);
|
|
175
|
+
|
|
176
|
+
// ハイフンが存在する場合のみサービスラベルを生成
|
|
177
|
+
if (repoName.includes('-')) {
|
|
178
|
+
const parts = repoName.split('-');
|
|
179
|
+
const servicePart = parts[parts.length - 1];
|
|
180
|
+
const serviceLabel = servicePart.toLowerCase().replace(/[^a-z0-9-]/g, '');
|
|
181
|
+
|
|
182
|
+
// サービスラベルがプロジェクトラベルと異なる場合のみ追加
|
|
183
|
+
if (serviceLabel !== projectLabel) {
|
|
184
|
+
labelSet.add(`service:${serviceLabel}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return Array.from(labelSet);
|
|
189
|
+
})();
|
|
133
190
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
191
|
+
const projectJson = {
|
|
192
|
+
projectId: repoName,
|
|
193
|
+
projectName: config.projectName,
|
|
194
|
+
jiraProjectKey: config.jiraKey,
|
|
195
|
+
confluenceLabels: labels,
|
|
196
|
+
status: 'active',
|
|
197
|
+
team: [],
|
|
198
|
+
stakeholders: ['@企画', '@部長'],
|
|
199
|
+
repository: repoUrl,
|
|
200
|
+
description: `${config.projectName}の開発`
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
writeFileSync('.kiro/project.json', JSON.stringify(projectJson, null, 2));
|
|
204
|
+
console.log(' ✅ project.json created');
|
|
205
|
+
|
|
206
|
+
// Step 8: Michiから共通ファイルをコピー
|
|
207
|
+
console.log('\n📋 Step 8: Copying common files from Michi...');
|
|
208
|
+
const michiPath = resolve(__dirname, '..');
|
|
139
209
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
210
|
+
// コピー先ディレクトリを事前に作成(actualProjectDir 配下に作成)
|
|
211
|
+
console.log(` 📂 Copying to: ${actualProjectDir}`);
|
|
212
|
+
mkdirSync(join(actualProjectDir, '.cursor/rules'), { recursive: true });
|
|
213
|
+
mkdirSync(join(actualProjectDir, '.cursor/commands/kiro'), { recursive: true });
|
|
214
|
+
mkdirSync(join(actualProjectDir, '.kiro/steering'), { recursive: true });
|
|
215
|
+
mkdirSync(join(actualProjectDir, '.kiro/settings/templates'), { recursive: true });
|
|
216
|
+
mkdirSync(join(actualProjectDir, 'scripts/utils'), { recursive: true });
|
|
217
|
+
|
|
218
|
+
// ルールファイル
|
|
219
|
+
const rulesToCopy = [
|
|
220
|
+
'multi-project.mdc',
|
|
221
|
+
'github-ssot.mdc',
|
|
222
|
+
'atlassian-mcp.mdc'
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
for (const rule of rulesToCopy) {
|
|
226
|
+
const src = join(michiPath, '.cursor/rules', rule);
|
|
227
|
+
const dest = join(actualProjectDir, '.cursor/rules', rule);
|
|
228
|
+
if (existsSync(src)) {
|
|
229
|
+
cpSync(src, dest);
|
|
230
|
+
console.log(` ✅ Copied: .cursor/rules/${rule}`);
|
|
143
231
|
}
|
|
144
232
|
}
|
|
145
233
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
projectId: repoName,
|
|
151
|
-
projectName: config.projectName,
|
|
152
|
-
jiraProjectKey: config.jiraKey,
|
|
153
|
-
confluenceLabels: labels,
|
|
154
|
-
status: 'active',
|
|
155
|
-
team: [],
|
|
156
|
-
stakeholders: ['@企画', '@部長'],
|
|
157
|
-
repository: repoUrl,
|
|
158
|
-
description: `${config.projectName}の開発`
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
writeFileSync('.kiro/project.json', JSON.stringify(projectJson, null, 2));
|
|
162
|
-
console.log(' ✅ project.json created');
|
|
163
|
-
|
|
164
|
-
// Step 6: Michiから共通ファイルをコピー
|
|
165
|
-
console.log('\n📋 Step 6: Copying common files from Michi...');
|
|
166
|
-
const michiPath = resolve(__dirname, '..');
|
|
167
|
-
|
|
168
|
-
// コピー先ディレクトリを事前に作成
|
|
169
|
-
mkdirSync('.cursor/rules', { recursive: true });
|
|
170
|
-
mkdirSync('.cursor/commands/kiro', { recursive: true });
|
|
171
|
-
mkdirSync('.kiro/steering', { recursive: true });
|
|
172
|
-
mkdirSync('.kiro/settings/templates', { recursive: true });
|
|
173
|
-
mkdirSync('scripts/utils', { recursive: true });
|
|
174
|
-
|
|
175
|
-
// ルールファイル
|
|
176
|
-
const rulesToCopy = [
|
|
177
|
-
'multi-project.mdc',
|
|
178
|
-
'github-ssot.mdc',
|
|
179
|
-
'atlassian-mcp.mdc'
|
|
180
|
-
];
|
|
181
|
-
|
|
182
|
-
for (const rule of rulesToCopy) {
|
|
183
|
-
const src = join(michiPath, '.cursor/rules', rule);
|
|
184
|
-
const dest = join(projectDir, '.cursor/rules', rule);
|
|
185
|
-
if (existsSync(src)) {
|
|
186
|
-
cpSync(src, dest);
|
|
187
|
-
console.log(` ✅ Copied: .cursor/rules/${rule}`);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// カスタムコマンド
|
|
192
|
-
const commandsToCopy = [
|
|
193
|
-
'confluence-sync.md',
|
|
194
|
-
'project-switch.md'
|
|
195
|
-
];
|
|
196
|
-
|
|
197
|
-
for (const cmd of commandsToCopy) {
|
|
198
|
-
const src = join(michiPath, '.cursor/commands/kiro', cmd);
|
|
199
|
-
const dest = join(projectDir, '.cursor/commands/kiro', cmd);
|
|
200
|
-
if (existsSync(src)) {
|
|
201
|
-
cpSync(src, dest);
|
|
202
|
-
console.log(` ✅ Copied: .cursor/commands/kiro/${cmd}`);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Steering
|
|
207
|
-
const steeringDir = join(michiPath, '.kiro/steering');
|
|
208
|
-
if (existsSync(steeringDir)) {
|
|
209
|
-
mkdirSync('.kiro/steering', { recursive: true });
|
|
210
|
-
cpSync(steeringDir, '.kiro/steering', { recursive: true });
|
|
211
|
-
console.log(' ✅ Copied: .kiro/steering/');
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Scripts(必要なスクリプトのみコピー)
|
|
215
|
-
const scriptsDir = join(michiPath, 'scripts');
|
|
216
|
-
if (existsSync(scriptsDir)) {
|
|
217
|
-
const scriptsToCopy = [
|
|
218
|
-
'confluence-sync.ts',
|
|
219
|
-
'jira-sync.ts',
|
|
220
|
-
'pr-automation.ts',
|
|
221
|
-
'markdown-to-confluence.ts',
|
|
222
|
-
'workflow-orchestrator.ts',
|
|
223
|
-
'list-projects.ts',
|
|
224
|
-
'resource-dashboard.ts',
|
|
225
|
-
'multi-project-estimate.ts',
|
|
226
|
-
'utils/project-meta.ts'
|
|
234
|
+
// カスタムコマンド
|
|
235
|
+
const commandsToCopy = [
|
|
236
|
+
'confluence-sync.md',
|
|
237
|
+
'project-switch.md'
|
|
227
238
|
];
|
|
228
239
|
|
|
229
|
-
for (const
|
|
230
|
-
const src = join(
|
|
231
|
-
const dest = join(
|
|
240
|
+
for (const cmd of commandsToCopy) {
|
|
241
|
+
const src = join(michiPath, '.cursor/commands/kiro', cmd);
|
|
242
|
+
const dest = join(actualProjectDir, '.cursor/commands/kiro', cmd);
|
|
232
243
|
if (existsSync(src)) {
|
|
233
|
-
// ディレクトリが必要な場合は作成
|
|
234
|
-
const destDir = dirname(dest);
|
|
235
|
-
mkdirSync(destDir, { recursive: true });
|
|
236
244
|
cpSync(src, dest);
|
|
237
|
-
console.log(` ✅ Copied:
|
|
245
|
+
console.log(` ✅ Copied: .cursor/commands/kiro/${cmd}`);
|
|
238
246
|
}
|
|
239
247
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
cpSync(
|
|
247
|
-
console.log(
|
|
248
|
+
|
|
249
|
+
// Steering
|
|
250
|
+
const steeringDir = join(michiPath, '.kiro/steering');
|
|
251
|
+
if (existsSync(steeringDir)) {
|
|
252
|
+
const destSteeringDir = join(actualProjectDir, '.kiro/steering');
|
|
253
|
+
mkdirSync(destSteeringDir, { recursive: true });
|
|
254
|
+
cpSync(steeringDir, destSteeringDir, { recursive: true });
|
|
255
|
+
console.log(' ✅ Copied: .kiro/steering/');
|
|
248
256
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
257
|
+
|
|
258
|
+
// Scripts(必要なスクリプトのみコピー)
|
|
259
|
+
const scriptsDir = join(michiPath, 'scripts');
|
|
260
|
+
if (existsSync(scriptsDir)) {
|
|
261
|
+
const scriptsToCopy = [
|
|
262
|
+
'confluence-sync.ts',
|
|
263
|
+
'jira-sync.ts',
|
|
264
|
+
'pr-automation.ts',
|
|
265
|
+
'markdown-to-confluence.ts',
|
|
266
|
+
'workflow-orchestrator.ts',
|
|
267
|
+
'list-projects.ts',
|
|
268
|
+
'resource-dashboard.ts',
|
|
269
|
+
'multi-project-estimate.ts',
|
|
270
|
+
'utils/project-meta.ts'
|
|
271
|
+
];
|
|
272
|
+
|
|
273
|
+
for (const script of scriptsToCopy) {
|
|
274
|
+
const src = join(scriptsDir, script);
|
|
275
|
+
const dest = join(actualProjectDir, 'scripts', script);
|
|
276
|
+
if (existsSync(src)) {
|
|
277
|
+
// ディレクトリが必要な場合は作成
|
|
278
|
+
const destDir = dirname(dest);
|
|
279
|
+
mkdirSync(destDir, { recursive: true });
|
|
280
|
+
cpSync(src, dest);
|
|
281
|
+
console.log(` ✅ Copied: scripts/${script}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// package.json, tsconfig.jsonをリポジトリルートにコピー
|
|
287
|
+
['package.json', 'tsconfig.json'].forEach(file => {
|
|
288
|
+
const src = join(michiPath, file);
|
|
289
|
+
const dest = join(projectDir, file); // リポジトリルートにコピー
|
|
290
|
+
if (existsSync(src)) {
|
|
291
|
+
cpSync(src, dest);
|
|
292
|
+
console.log(` ✅ Copied: ${file} (to repository root)`);
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Step 9: npm install(リポジトリルートで実行)
|
|
297
|
+
// 注意: setup:env は bash スクリプトなので依存関係不要だが、
|
|
298
|
+
// 将来的に setup:env が tsx やローカルスクリプトに依存する場合は
|
|
299
|
+
// npm install を先に実行する必要があるため、順序を入れ替え
|
|
300
|
+
console.log('\n📦 Step 9: Installing dependencies...');
|
|
301
|
+
process.chdir(projectDir);
|
|
302
|
+
execSync('npm install', { stdio: 'inherit' });
|
|
303
|
+
console.log(' ✅ Dependencies installed');
|
|
304
|
+
|
|
305
|
+
// Step 10: .env テンプレート作成(プロジェクトディレクトリで実行)
|
|
306
|
+
console.log('\n🔐 Step 10: Creating .env template...');
|
|
307
|
+
// package.jsonはprojectDirにあるため、そこで実行
|
|
308
|
+
execSync('npm run setup:env', { cwd: actualProjectDir, stdio: 'inherit' });
|
|
309
|
+
console.log(' ✅ .env created');
|
|
310
|
+
|
|
311
|
+
// Step 11: 初期コミット
|
|
312
|
+
console.log('\n💾 Step 11: Creating initial commit...');
|
|
313
|
+
|
|
314
|
+
const commitMessage = `chore: プロジェクト初期化
|
|
264
315
|
|
|
265
316
|
- cc-sdd導入
|
|
266
317
|
- プロジェクトメタデータ設定(${config.jiraKey})
|
|
267
318
|
- 自動化スクリプト追加
|
|
268
|
-
- Confluence/JIRA
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
319
|
+
- Confluence/JIRA連携設定`;
|
|
320
|
+
|
|
321
|
+
if (deps.vcs === 'jj') {
|
|
322
|
+
execSync(`jj commit -m "${commitMessage}"`, { stdio: 'inherit' });
|
|
323
|
+
execSync('jj bookmark create main -r "@-"', { stdio: 'inherit' });
|
|
324
|
+
} else {
|
|
325
|
+
execSync('git add .', { stdio: 'inherit' });
|
|
326
|
+
execSync(`git commit -m "${commitMessage}"`, { stdio: 'inherit' });
|
|
327
|
+
execSync('git branch -M main', { stdio: 'inherit' });
|
|
328
|
+
}
|
|
329
|
+
console.log(' ✅ Initial commit created');
|
|
330
|
+
|
|
331
|
+
// 完了メッセージ
|
|
332
|
+
console.log('\n');
|
|
333
|
+
console.log('🎉 プロジェクトセットアップ完了!');
|
|
334
|
+
console.log('');
|
|
335
|
+
console.log('次のステップ:');
|
|
336
|
+
console.log(` 1. cd ${actualProjectDir}`);
|
|
337
|
+
console.log(' 2. .env ファイルを編集して認証情報を設定');
|
|
338
|
+
console.log(' 3. jj git push --bookmark main --allow-new');
|
|
339
|
+
console.log(' 4. Cursor で開く: cursor .');
|
|
340
|
+
console.log(' 5. /kiro:spec-init <機能説明> で開発開始');
|
|
341
|
+
console.log('');
|
|
342
|
+
console.log('詳細: docs/new-repository-setup.md');
|
|
343
|
+
|
|
344
|
+
} finally {
|
|
345
|
+
// 元の作業ディレクトリに戻る
|
|
346
|
+
process.chdir(originalCwd);
|
|
347
|
+
}
|
|
285
348
|
}
|
|
286
349
|
|
|
287
350
|
// 実行
|
package/scripts/jira-sync.ts
CHANGED
|
@@ -25,6 +25,7 @@ import { loadProjectMeta } from './utils/project-meta.js';
|
|
|
25
25
|
import { validateFeatureNameOrThrow } from './utils/feature-name-validator.js';
|
|
26
26
|
import { getConfig, getConfigPath } from './utils/config-loader.js';
|
|
27
27
|
import { validateForJiraSync } from './utils/config-validator.js';
|
|
28
|
+
import { updateSpecJsonAfterJiraSync } from './utils/spec-updater.js';
|
|
28
29
|
|
|
29
30
|
config();
|
|
30
31
|
|
|
@@ -617,10 +618,24 @@ async function syncTasksToJIRA(featureName: string): Promise<void> {
|
|
|
617
618
|
// 新規作成数と再利用数を正確に計算
|
|
618
619
|
const newStoryCount = createdStories.filter(key => !existingStoryKeys.has(key)).length;
|
|
619
620
|
const reusedStoryCount = createdStories.filter(key => existingStoryKeys.has(key)).length;
|
|
620
|
-
|
|
621
|
+
|
|
621
622
|
console.log('\n✅ JIRA sync completed');
|
|
622
623
|
console.log(` Epic: ${epic.key}`);
|
|
623
624
|
console.log(` Stories: ${createdStories.length} processed (${newStoryCount} new, ${reusedStoryCount} reused)`);
|
|
625
|
+
|
|
626
|
+
// spec.json を更新
|
|
627
|
+
const jiraBaseUrl = process.env.ATLASSIAN_URL || '';
|
|
628
|
+
try {
|
|
629
|
+
updateSpecJsonAfterJiraSync(featureName, {
|
|
630
|
+
projectKey: projectMeta.jiraProjectKey,
|
|
631
|
+
epicKey: epic.key,
|
|
632
|
+
epicUrl: `${jiraBaseUrl}/browse/${epic.key}`,
|
|
633
|
+
storyKeys: createdStories
|
|
634
|
+
});
|
|
635
|
+
} catch (error) {
|
|
636
|
+
console.warn(`⚠️ Failed to update spec.json after JIRA sync: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
637
|
+
// spec.json更新の失敗はスクリプト全体の失敗とはしない(JIRA同期は成功しているため)
|
|
638
|
+
}
|
|
624
639
|
}
|
|
625
640
|
|
|
626
641
|
// CLI実行
|
package/scripts/list-projects.ts
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
import { Octokit } from '@octokit/rest';
|
|
7
7
|
import { config } from 'dotenv';
|
|
8
|
-
import axios from 'axios';
|
|
9
8
|
|
|
10
9
|
config();
|
|
11
10
|
|
|
@@ -20,42 +19,70 @@ interface ProjectInfo {
|
|
|
20
19
|
async function listProjects(): Promise<void> {
|
|
21
20
|
const token = process.env.GITHUB_TOKEN;
|
|
22
21
|
const org = process.env.GITHUB_ORG;
|
|
23
|
-
|
|
22
|
+
|
|
24
23
|
if (!token || !org) {
|
|
25
24
|
throw new Error('Missing GitHub credentials');
|
|
26
25
|
}
|
|
27
|
-
|
|
26
|
+
|
|
28
27
|
const octokit = new Octokit({ auth: token });
|
|
29
|
-
|
|
28
|
+
|
|
30
29
|
console.log(`Fetching projects for organization: ${org}`);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
|
|
31
|
+
// ページネーション対応:全リポジトリを取得
|
|
32
|
+
const repos = await octokit.paginate(octokit.repos.listForOrg, {
|
|
33
|
+
org,
|
|
34
|
+
per_page: 100
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
console.log(`Found ${repos.length} repositories`);
|
|
38
|
+
|
|
34
39
|
const projects: ProjectInfo[] = [];
|
|
35
|
-
|
|
40
|
+
|
|
36
41
|
for (const repo of repos) {
|
|
37
42
|
try {
|
|
38
|
-
//
|
|
39
|
-
const
|
|
43
|
+
// ページネーション対応:projects/ディレクトリの全コンテンツを取得
|
|
44
|
+
const projectsDir = await octokit.paginate('GET /repos/{owner}/{repo}/contents/{path}', {
|
|
40
45
|
owner: org,
|
|
41
46
|
repo: repo.name,
|
|
42
|
-
path: '
|
|
47
|
+
path: 'projects',
|
|
48
|
+
per_page: 100
|
|
43
49
|
});
|
|
44
|
-
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
|
|
51
|
+
if (Array.isArray(projectsDir)) {
|
|
52
|
+
// projects/配下の各プロジェクトディレクトリを処理
|
|
53
|
+
for (const projectEntry of projectsDir) {
|
|
54
|
+
if (typeof projectEntry === 'object' && projectEntry !== null && 'type' in projectEntry && projectEntry.type === 'dir' && 'name' in projectEntry) {
|
|
55
|
+
try {
|
|
56
|
+
// projects/{project-id}/.kiro/project.json を取得
|
|
57
|
+
const { data } = await octokit.repos.getContent({
|
|
58
|
+
owner: org,
|
|
59
|
+
repo: repo.name,
|
|
60
|
+
path: `projects/${(projectEntry as any).name}/.kiro/project.json`
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if ('content' in data) {
|
|
64
|
+
const content = Buffer.from(data.content, 'base64').toString('utf-8');
|
|
65
|
+
const projectMeta = JSON.parse(content);
|
|
66
|
+
|
|
67
|
+
projects.push({
|
|
68
|
+
name: projectMeta.projectName,
|
|
69
|
+
projectId: projectMeta.projectId,
|
|
70
|
+
status: projectMeta.status,
|
|
71
|
+
jiraKey: projectMeta.jiraProjectKey,
|
|
72
|
+
team: projectMeta.team
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
} catch (error) {
|
|
76
|
+
// プロジェクトディレクトリに.kiro/project.jsonがない場合はスキップ
|
|
77
|
+
console.warn(`⚠️ Skipping project ${(projectEntry as any).name} in ${repo.name}:`, error instanceof Error ? error.message : 'Unknown error');
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
56
82
|
}
|
|
57
83
|
} catch (error) {
|
|
58
|
-
//
|
|
84
|
+
// projects/ディレクトリが存在しない、または API エラーの場合はスキップ
|
|
85
|
+
console.warn(`⚠️ Skipping ${repo.name}:`, error instanceof Error ? error.message : 'Unknown error');
|
|
59
86
|
continue;
|
|
60
87
|
}
|
|
61
88
|
}
|