@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
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* 新規プロジェクト自動セットアップスクリプト
|
|
4
4
|
*
|
|
5
|
+
* Issue #35: cc-sdd準拠の多環境対応基盤
|
|
6
|
+
* - templates/ディレクトリから読み込み
|
|
7
|
+
* - プレースホルダーはそのまま(実行時にAIが解釈)
|
|
8
|
+
*
|
|
5
9
|
* 使い方:
|
|
6
10
|
* npm run create-project -- \
|
|
7
11
|
* --name "20240115-payment-api" \
|
|
@@ -11,9 +15,10 @@
|
|
|
11
15
|
|
|
12
16
|
import { execSync } from 'child_process';
|
|
13
17
|
import { writeFileSync, mkdirSync, cpSync, existsSync } from 'fs';
|
|
14
|
-
import { resolve, join, dirname } from 'path';
|
|
18
|
+
import { resolve, join, dirname, basename } from 'path';
|
|
15
19
|
import { fileURLToPath } from 'url';
|
|
16
20
|
import { config as loadDotenv } from 'dotenv';
|
|
21
|
+
import { findTemplateFile, validateRequiredTemplates } from './utils/template-finder.js';
|
|
17
22
|
|
|
18
23
|
// ESモジュール対応: __dirnameの代替
|
|
19
24
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -67,6 +72,25 @@ function parseArgs(): ProjectConfig {
|
|
|
67
72
|
return config as ProjectConfig;
|
|
68
73
|
}
|
|
69
74
|
|
|
75
|
+
/**
|
|
76
|
+
* jj/git の依存性をチェックし、利用可能なVCSを返す
|
|
77
|
+
*/
|
|
78
|
+
function checkDependencies(): { vcs: 'jj' | 'git'; version: string } {
|
|
79
|
+
try {
|
|
80
|
+
const jjVersion = execSync('jj --version', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
81
|
+
return { vcs: 'jj', version: jjVersion };
|
|
82
|
+
} catch {
|
|
83
|
+
try {
|
|
84
|
+
const gitVersion = execSync('git --version', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
85
|
+
return { vcs: 'git', version: gitVersion };
|
|
86
|
+
} catch {
|
|
87
|
+
console.error('❌ Neither jj nor git is installed');
|
|
88
|
+
console.error(' Please install jj (https://github.com/martinvonz/jj) or git');
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
70
94
|
async function createProject(config: ProjectConfig): Promise<void> {
|
|
71
95
|
const org = config.org || process.env.GITHUB_ORG;
|
|
72
96
|
|
|
@@ -79,10 +103,14 @@ async function createProject(config: ProjectConfig): Promise<void> {
|
|
|
79
103
|
const repoName = config.name;
|
|
80
104
|
const repoUrl = `https://github.com/${org}/${repoName}`;
|
|
81
105
|
const projectDir = resolve(`../${repoName}`);
|
|
82
|
-
|
|
106
|
+
|
|
107
|
+
// VCS依存性チェック
|
|
108
|
+
const deps = checkDependencies();
|
|
109
|
+
|
|
83
110
|
console.log(`🚀 Creating new project: ${config.projectName}`);
|
|
84
111
|
console.log(` Repository: ${org}/${repoName}`);
|
|
85
112
|
console.log(` JIRA: ${config.jiraKey}`);
|
|
113
|
+
console.log(` VCS: ${deps.vcs} (${deps.version})`);
|
|
86
114
|
console.log('');
|
|
87
115
|
|
|
88
116
|
// Step 1: GitHubリポジトリ作成
|
|
@@ -93,16 +121,20 @@ async function createProject(config: ProjectConfig): Promise<void> {
|
|
|
93
121
|
{ stdio: 'inherit' }
|
|
94
122
|
);
|
|
95
123
|
console.log(' ✅ Repository created');
|
|
96
|
-
} catch
|
|
124
|
+
} catch {
|
|
97
125
|
console.log(' ⚠️ Repository may already exist');
|
|
98
126
|
}
|
|
99
127
|
|
|
100
128
|
// Step 2: リポジトリクローン
|
|
101
129
|
console.log('\n📥 Step 2: Cloning repository...');
|
|
102
130
|
try {
|
|
103
|
-
|
|
131
|
+
if (deps.vcs === 'jj') {
|
|
132
|
+
execSync(`jj git clone ${repoUrl} ${projectDir}`, { stdio: 'inherit' });
|
|
133
|
+
} else {
|
|
134
|
+
execSync(`git clone ${repoUrl} ${projectDir}`, { stdio: 'inherit' });
|
|
135
|
+
}
|
|
104
136
|
console.log(' ✅ Repository cloned');
|
|
105
|
-
} catch
|
|
137
|
+
} catch {
|
|
106
138
|
console.log(' ⚠️ Clone failed, checking if directory exists...');
|
|
107
139
|
}
|
|
108
140
|
|
|
@@ -113,175 +145,236 @@ async function createProject(config: ProjectConfig): Promise<void> {
|
|
|
113
145
|
process.exit(1);
|
|
114
146
|
}
|
|
115
147
|
|
|
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');
|
|
148
|
+
// 元の作業ディレクトリを保存
|
|
149
|
+
const originalCwd = process.cwd();
|
|
123
150
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
151
|
+
try {
|
|
152
|
+
process.chdir(projectDir);
|
|
153
|
+
console.log(` 📂 Working directory: ${projectDir}`);
|
|
154
|
+
|
|
155
|
+
// Step 4: projects/ディレクトリとプロジェクトディレクトリを作成
|
|
156
|
+
console.log('\n📁 Step 4: Creating project directory structure...');
|
|
157
|
+
const projectsDir = join(projectDir, 'projects');
|
|
158
|
+
const actualProjectDir = join(projectsDir, repoName);
|
|
159
|
+
mkdirSync(actualProjectDir, { recursive: true });
|
|
160
|
+
console.log(` ✅ Project directory created: ${actualProjectDir}`);
|
|
161
|
+
|
|
162
|
+
// Step 5: プロジェクトディレクトリに移動
|
|
163
|
+
process.chdir(actualProjectDir);
|
|
164
|
+
console.log(` 📂 Working directory: ${actualProjectDir}`);
|
|
165
|
+
|
|
166
|
+
// Step 6: cc-sdd導入
|
|
167
|
+
console.log('\n⚙️ Step 6: Installing cc-sdd...');
|
|
168
|
+
execSync('npx cc-sdd@latest --cursor --lang ja --yes', { stdio: 'inherit' });
|
|
169
|
+
console.log(' ✅ cc-sdd installed');
|
|
170
|
+
|
|
171
|
+
// Step 7: .kiro/project.json 作成
|
|
172
|
+
console.log('\n📝 Step 7: Creating project metadata...');
|
|
173
|
+
mkdirSync('.kiro', { recursive: true });
|
|
139
174
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
175
|
+
// ラベル生成(安全なフォールバック付き)
|
|
176
|
+
const labels = config.labels || (() => {
|
|
177
|
+
// プロジェクトIDからプロジェクトラベル生成
|
|
178
|
+
const projectLabel = repoName.toLowerCase().replace(/[^a-z0-9-]/g, '');
|
|
179
|
+
const labelSet = new Set([`project:${projectLabel}`]);
|
|
180
|
+
|
|
181
|
+
// ハイフンが存在する場合のみサービスラベルを生成
|
|
182
|
+
if (repoName.includes('-')) {
|
|
183
|
+
const parts = repoName.split('-');
|
|
184
|
+
const servicePart = parts[parts.length - 1];
|
|
185
|
+
const serviceLabel = servicePart.toLowerCase().replace(/[^a-z0-9-]/g, '');
|
|
186
|
+
|
|
187
|
+
// サービスラベルがプロジェクトラベルと異なる場合のみ追加
|
|
188
|
+
if (serviceLabel !== projectLabel) {
|
|
189
|
+
labelSet.add(`service:${serviceLabel}`);
|
|
190
|
+
}
|
|
143
191
|
}
|
|
144
|
-
|
|
192
|
+
|
|
193
|
+
return Array.from(labelSet);
|
|
194
|
+
})();
|
|
145
195
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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}`);
|
|
196
|
+
const projectJson = {
|
|
197
|
+
projectId: repoName,
|
|
198
|
+
projectName: config.projectName,
|
|
199
|
+
jiraProjectKey: config.jiraKey,
|
|
200
|
+
confluenceLabels: labels,
|
|
201
|
+
status: 'active',
|
|
202
|
+
team: [],
|
|
203
|
+
stakeholders: ['@企画', '@部長'],
|
|
204
|
+
repository: repoUrl,
|
|
205
|
+
description: `${config.projectName}の開発`
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
writeFileSync('.kiro/project.json', JSON.stringify(projectJson, null, 2));
|
|
209
|
+
console.log(' ✅ project.json created');
|
|
210
|
+
|
|
211
|
+
// Step 8: Michiから共通ファイルをコピー(templates/から)
|
|
212
|
+
console.log('\n📋 Step 8: Copying common files from Michi templates...');
|
|
213
|
+
console.log(' ℹ️ Issue #35: cc-sdd compliant approach (placeholders preserved)');
|
|
214
|
+
const michiPath = resolve(__dirname, '..');
|
|
215
|
+
|
|
216
|
+
// 必須テンプレートのバリデーション
|
|
217
|
+
const requiredTemplates = [
|
|
218
|
+
'rules/github-ssot.mdc',
|
|
219
|
+
'rules/multi-project.mdc',
|
|
220
|
+
'commands/michi/confluence-sync.md',
|
|
221
|
+
'commands/michi/project-switch.md'
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
validateRequiredTemplates(michiPath, requiredTemplates);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.error('\n❌ Template validation failed:');
|
|
228
|
+
console.error(` ${error instanceof Error ? error.message : String(error)}`);
|
|
229
|
+
process.exit(1);
|
|
188
230
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
'
|
|
194
|
-
'
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
231
|
+
|
|
232
|
+
// コピー先ディレクトリを事前に作成(actualProjectDir 配下に作成)
|
|
233
|
+
console.log(` 📂 Copying to: ${actualProjectDir}`);
|
|
234
|
+
mkdirSync(join(actualProjectDir, '.cursor/rules'), { recursive: true });
|
|
235
|
+
mkdirSync(join(actualProjectDir, '.cursor/commands/michi'), { recursive: true });
|
|
236
|
+
mkdirSync(join(actualProjectDir, '.kiro/steering'), { recursive: true });
|
|
237
|
+
mkdirSync(join(actualProjectDir, '.kiro/settings/templates'), { recursive: true });
|
|
238
|
+
mkdirSync(join(actualProjectDir, 'scripts/utils'), { recursive: true });
|
|
239
|
+
|
|
240
|
+
// ルールファイル(templates/から)
|
|
241
|
+
const rulesToCopy = [
|
|
242
|
+
'rules/multi-project.mdc',
|
|
243
|
+
'rules/github-ssot.mdc',
|
|
244
|
+
'rules/atlassian-mcp.mdc'
|
|
245
|
+
];
|
|
246
|
+
|
|
247
|
+
for (const rulePath of rulesToCopy) {
|
|
248
|
+
const src = findTemplateFile(michiPath, rulePath);
|
|
249
|
+
const fileName = basename(rulePath);
|
|
250
|
+
const dest = join(actualProjectDir, '.cursor/rules', fileName);
|
|
251
|
+
|
|
252
|
+
if (src) {
|
|
253
|
+
cpSync(src, dest);
|
|
254
|
+
console.log(` ✅ Copied: .cursor/rules/${fileName} (from templates/)`);
|
|
255
|
+
} else {
|
|
256
|
+
console.log(` ⚠️ ${fileName} not found in templates/`);
|
|
257
|
+
}
|
|
203
258
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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'
|
|
259
|
+
|
|
260
|
+
// カスタムコマンド(templates/から)
|
|
261
|
+
const commandsToCopy = [
|
|
262
|
+
'commands/michi/confluence-sync.md',
|
|
263
|
+
'commands/michi/project-switch.md'
|
|
227
264
|
];
|
|
228
265
|
|
|
229
|
-
for (const
|
|
230
|
-
const src =
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
mkdirSync(destDir, { recursive: true });
|
|
266
|
+
for (const cmdPath of commandsToCopy) {
|
|
267
|
+
const src = findTemplateFile(michiPath, cmdPath);
|
|
268
|
+
const fileName = basename(cmdPath);
|
|
269
|
+
const dest = join(actualProjectDir, '.cursor/commands/michi', fileName);
|
|
270
|
+
|
|
271
|
+
if (src) {
|
|
236
272
|
cpSync(src, dest);
|
|
237
|
-
console.log(` ✅ Copied:
|
|
273
|
+
console.log(` ✅ Copied: .cursor/commands/michi/${fileName} (from templates/)`);
|
|
274
|
+
} else {
|
|
275
|
+
console.log(` ⚠️ ${fileName} not found in templates/`);
|
|
238
276
|
}
|
|
239
277
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
cpSync(
|
|
247
|
-
console.log(
|
|
278
|
+
|
|
279
|
+
// Steering
|
|
280
|
+
const steeringDir = join(michiPath, '.kiro/steering');
|
|
281
|
+
if (existsSync(steeringDir)) {
|
|
282
|
+
const destSteeringDir = join(actualProjectDir, '.kiro/steering');
|
|
283
|
+
mkdirSync(destSteeringDir, { recursive: true });
|
|
284
|
+
cpSync(steeringDir, destSteeringDir, { recursive: true });
|
|
285
|
+
console.log(' ✅ Copied: .kiro/steering/');
|
|
248
286
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
287
|
+
|
|
288
|
+
// Scripts(必要なスクリプトのみコピー)
|
|
289
|
+
const scriptsDir = join(michiPath, 'scripts');
|
|
290
|
+
if (existsSync(scriptsDir)) {
|
|
291
|
+
const scriptsToCopy = [
|
|
292
|
+
'confluence-sync.ts',
|
|
293
|
+
'jira-sync.ts',
|
|
294
|
+
'pr-automation.ts',
|
|
295
|
+
'markdown-to-confluence.ts',
|
|
296
|
+
'workflow-orchestrator.ts',
|
|
297
|
+
'list-projects.ts',
|
|
298
|
+
'resource-dashboard.ts',
|
|
299
|
+
'multi-project-estimate.ts',
|
|
300
|
+
'utils/project-meta.ts'
|
|
301
|
+
];
|
|
302
|
+
|
|
303
|
+
for (const script of scriptsToCopy) {
|
|
304
|
+
const src = join(scriptsDir, script);
|
|
305
|
+
const dest = join(actualProjectDir, 'scripts', script);
|
|
306
|
+
if (existsSync(src)) {
|
|
307
|
+
// ディレクトリが必要な場合は作成
|
|
308
|
+
const destDir = dirname(dest);
|
|
309
|
+
mkdirSync(destDir, { recursive: true });
|
|
310
|
+
cpSync(src, dest);
|
|
311
|
+
console.log(` ✅ Copied: scripts/${script}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// package.json, tsconfig.jsonをリポジトリルートにコピー
|
|
317
|
+
['package.json', 'tsconfig.json'].forEach(file => {
|
|
318
|
+
const src = join(michiPath, file);
|
|
319
|
+
const dest = join(projectDir, file); // リポジトリルートにコピー
|
|
320
|
+
if (existsSync(src)) {
|
|
321
|
+
cpSync(src, dest);
|
|
322
|
+
console.log(` ✅ Copied: ${file} (to repository root)`);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// Step 9: npm install(リポジトリルートで実行)
|
|
327
|
+
// 注意: setup:env は bash スクリプトなので依存関係不要だが、
|
|
328
|
+
// 将来的に setup:env が tsx やローカルスクリプトに依存する場合は
|
|
329
|
+
// npm install を先に実行する必要があるため、順序を入れ替え
|
|
330
|
+
console.log('\n📦 Step 9: Installing dependencies...');
|
|
331
|
+
process.chdir(projectDir);
|
|
332
|
+
execSync('npm install', { stdio: 'inherit' });
|
|
333
|
+
console.log(' ✅ Dependencies installed');
|
|
334
|
+
|
|
335
|
+
// Step 10: .env テンプレート作成(プロジェクトディレクトリで実行)
|
|
336
|
+
console.log('\n🔐 Step 10: Creating .env template...');
|
|
337
|
+
// package.jsonはprojectDirにあるため、そこで実行
|
|
338
|
+
execSync('npm run setup:env', { cwd: actualProjectDir, stdio: 'inherit' });
|
|
339
|
+
console.log(' ✅ .env created');
|
|
340
|
+
|
|
341
|
+
// Step 11: 初期コミット
|
|
342
|
+
console.log('\n💾 Step 11: Creating initial commit...');
|
|
343
|
+
|
|
344
|
+
const commitMessage = `chore: プロジェクト初期化
|
|
264
345
|
|
|
265
346
|
- cc-sdd導入
|
|
266
347
|
- プロジェクトメタデータ設定(${config.jiraKey})
|
|
267
348
|
- 自動化スクリプト追加
|
|
268
|
-
- Confluence/JIRA
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
349
|
+
- Confluence/JIRA連携設定`;
|
|
350
|
+
|
|
351
|
+
if (deps.vcs === 'jj') {
|
|
352
|
+
execSync(`jj commit -m "${commitMessage}"`, { stdio: 'inherit' });
|
|
353
|
+
execSync('jj bookmark create main -r "@-"', { stdio: 'inherit' });
|
|
354
|
+
} else {
|
|
355
|
+
execSync('git add .', { stdio: 'inherit' });
|
|
356
|
+
execSync(`git commit -m "${commitMessage}"`, { stdio: 'inherit' });
|
|
357
|
+
execSync('git branch -M main', { stdio: 'inherit' });
|
|
358
|
+
}
|
|
359
|
+
console.log(' ✅ Initial commit created');
|
|
360
|
+
|
|
361
|
+
// 完了メッセージ
|
|
362
|
+
console.log('\n');
|
|
363
|
+
console.log('🎉 プロジェクトセットアップ完了!');
|
|
364
|
+
console.log('');
|
|
365
|
+
console.log('次のステップ:');
|
|
366
|
+
console.log(` 1. cd ${actualProjectDir}`);
|
|
367
|
+
console.log(' 2. .env ファイルを編集して認証情報を設定');
|
|
368
|
+
console.log(' 3. jj git push --bookmark main --allow-new');
|
|
369
|
+
console.log(' 4. Cursor で開く: cursor .');
|
|
370
|
+
console.log(' 5. /kiro:spec-init <機能説明> で開発開始');
|
|
371
|
+
console.log('');
|
|
372
|
+
console.log('詳細: docs/getting-started/new-repository-setup.md');
|
|
373
|
+
|
|
374
|
+
} finally {
|
|
375
|
+
// 元の作業ディレクトリに戻る
|
|
376
|
+
process.chdir(originalCwd);
|
|
377
|
+
}
|
|
285
378
|
}
|
|
286
379
|
|
|
287
380
|
// 実行
|
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
|
}
|