@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.
Files changed (175) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +205 -11
  3. package/dist/scripts/__tests__/create-project.test.d.ts +2 -0
  4. package/dist/scripts/__tests__/create-project.test.d.ts.map +1 -0
  5. package/dist/scripts/__tests__/create-project.test.js +247 -0
  6. package/dist/scripts/__tests__/create-project.test.js.map +1 -0
  7. package/dist/scripts/__tests__/multi-project-estimate.test.d.ts +2 -0
  8. package/dist/scripts/__tests__/multi-project-estimate.test.d.ts.map +1 -0
  9. package/dist/scripts/__tests__/multi-project-estimate.test.js +119 -0
  10. package/dist/scripts/__tests__/multi-project-estimate.test.js.map +1 -0
  11. package/dist/scripts/__tests__/setup-existing-project.test.d.ts +2 -0
  12. package/dist/scripts/__tests__/setup-existing-project.test.d.ts.map +1 -0
  13. package/dist/scripts/__tests__/setup-existing-project.test.js +129 -0
  14. package/dist/scripts/__tests__/setup-existing-project.test.js.map +1 -0
  15. package/dist/scripts/__tests__/setup-interactive.test.d.ts +2 -0
  16. package/dist/scripts/__tests__/setup-interactive.test.d.ts.map +1 -0
  17. package/dist/scripts/__tests__/setup-interactive.test.js +160 -0
  18. package/dist/scripts/__tests__/setup-interactive.test.js.map +1 -0
  19. package/dist/scripts/config/default-config.json +57 -0
  20. package/dist/scripts/confluence-sync.d.ts +4 -0
  21. package/dist/scripts/confluence-sync.d.ts.map +1 -1
  22. package/dist/scripts/confluence-sync.js +12 -23
  23. package/dist/scripts/confluence-sync.js.map +1 -1
  24. package/dist/scripts/constants/__tests__/environments.test.d.ts +2 -0
  25. package/dist/scripts/constants/__tests__/environments.test.d.ts.map +1 -0
  26. package/dist/scripts/constants/__tests__/environments.test.js +91 -0
  27. package/dist/scripts/constants/__tests__/environments.test.js.map +1 -0
  28. package/dist/scripts/constants/__tests__/languages.test.d.ts +2 -0
  29. package/dist/scripts/constants/__tests__/languages.test.d.ts.map +1 -0
  30. package/dist/scripts/constants/__tests__/languages.test.js +82 -0
  31. package/dist/scripts/constants/__tests__/languages.test.js.map +1 -0
  32. package/dist/scripts/constants/environments.d.ts +33 -0
  33. package/dist/scripts/constants/environments.d.ts.map +1 -0
  34. package/dist/scripts/constants/environments.js +49 -0
  35. package/dist/scripts/constants/environments.js.map +1 -0
  36. package/dist/scripts/constants/languages.d.ts +23 -0
  37. package/dist/scripts/constants/languages.d.ts.map +1 -0
  38. package/dist/scripts/constants/languages.js +53 -0
  39. package/dist/scripts/constants/languages.js.map +1 -0
  40. package/dist/scripts/create-project.d.ts +4 -0
  41. package/dist/scripts/create-project.d.ts.map +1 -1
  42. package/dist/scripts/create-project.js +227 -137
  43. package/dist/scripts/create-project.js.map +1 -1
  44. package/dist/scripts/jira-sync.d.ts.map +1 -1
  45. package/dist/scripts/jira-sync.js +15 -0
  46. package/dist/scripts/jira-sync.js.map +1 -1
  47. package/dist/scripts/list-projects.d.ts.map +1 -1
  48. package/dist/scripts/list-projects.js +42 -15
  49. package/dist/scripts/list-projects.js.map +1 -1
  50. package/dist/scripts/multi-project-estimate.d.ts.map +1 -1
  51. package/dist/scripts/multi-project-estimate.js +56 -21
  52. package/dist/scripts/multi-project-estimate.js.map +1 -1
  53. package/dist/scripts/resource-dashboard.d.ts.map +1 -1
  54. package/dist/scripts/resource-dashboard.js +74 -17
  55. package/dist/scripts/resource-dashboard.js.map +1 -1
  56. package/dist/scripts/setup-existing-project.d.ts +3 -1
  57. package/dist/scripts/setup-existing-project.d.ts.map +1 -1
  58. package/dist/scripts/setup-existing-project.js +306 -217
  59. package/dist/scripts/setup-existing-project.js.map +1 -1
  60. package/dist/scripts/setup-interactive.d.ts +10 -0
  61. package/dist/scripts/setup-interactive.d.ts.map +1 -0
  62. package/dist/scripts/setup-interactive.js +413 -0
  63. package/dist/scripts/setup-interactive.js.map +1 -0
  64. package/dist/scripts/template/__tests__/renderer.test.d.ts +2 -0
  65. package/dist/scripts/template/__tests__/renderer.test.d.ts.map +1 -0
  66. package/dist/scripts/template/__tests__/renderer.test.js +165 -0
  67. package/dist/scripts/template/__tests__/renderer.test.js.map +1 -0
  68. package/dist/scripts/template/renderer.d.ts +70 -0
  69. package/dist/scripts/template/renderer.d.ts.map +1 -0
  70. package/dist/scripts/template/renderer.js +99 -0
  71. package/dist/scripts/template/renderer.js.map +1 -0
  72. package/dist/scripts/utils/__tests__/config-validator.test.js +5 -0
  73. package/dist/scripts/utils/__tests__/config-validator.test.js.map +1 -1
  74. package/dist/scripts/utils/__tests__/spec-updater.test.d.ts +5 -0
  75. package/dist/scripts/utils/__tests__/spec-updater.test.d.ts.map +1 -0
  76. package/dist/scripts/utils/__tests__/spec-updater.test.js +158 -0
  77. package/dist/scripts/utils/__tests__/spec-updater.test.js.map +1 -0
  78. package/dist/scripts/utils/confluence-hierarchy.d.ts +2 -1
  79. package/dist/scripts/utils/confluence-hierarchy.d.ts.map +1 -1
  80. package/dist/scripts/utils/confluence-hierarchy.js +5 -0
  81. package/dist/scripts/utils/confluence-hierarchy.js.map +1 -1
  82. package/dist/scripts/utils/project-finder.d.ts +30 -0
  83. package/dist/scripts/utils/project-finder.d.ts.map +1 -0
  84. package/dist/scripts/utils/project-finder.js +147 -0
  85. package/dist/scripts/utils/project-finder.js.map +1 -0
  86. package/dist/scripts/utils/spec-updater.d.ts +72 -0
  87. package/dist/scripts/utils/spec-updater.d.ts.map +1 -0
  88. package/dist/scripts/utils/spec-updater.js +141 -0
  89. package/dist/scripts/utils/spec-updater.js.map +1 -0
  90. package/dist/scripts/utils/template-finder.d.ts +37 -0
  91. package/dist/scripts/utils/template-finder.d.ts.map +1 -0
  92. package/dist/scripts/utils/template-finder.js +63 -0
  93. package/dist/scripts/utils/template-finder.js.map +1 -0
  94. package/dist/src/__tests__/integration/setup/claude-agent.test.d.ts +5 -0
  95. package/dist/src/__tests__/integration/setup/claude-agent.test.d.ts.map +1 -0
  96. package/dist/src/__tests__/integration/setup/claude-agent.test.js +125 -0
  97. package/dist/src/__tests__/integration/setup/claude-agent.test.js.map +1 -0
  98. package/dist/src/__tests__/integration/setup/claude.test.d.ts +5 -0
  99. package/dist/src/__tests__/integration/setup/claude.test.d.ts.map +1 -0
  100. package/dist/src/__tests__/integration/setup/claude.test.js +111 -0
  101. package/dist/src/__tests__/integration/setup/claude.test.js.map +1 -0
  102. package/dist/src/__tests__/integration/setup/cursor.test.d.ts +5 -0
  103. package/dist/src/__tests__/integration/setup/cursor.test.d.ts.map +1 -0
  104. package/dist/src/__tests__/integration/setup/cursor.test.js +162 -0
  105. package/dist/src/__tests__/integration/setup/cursor.test.js.map +1 -0
  106. package/dist/src/__tests__/integration/setup/helpers/fs-assertions.d.ts +32 -0
  107. package/dist/src/__tests__/integration/setup/helpers/fs-assertions.d.ts.map +1 -0
  108. package/dist/src/__tests__/integration/setup/helpers/fs-assertions.js +72 -0
  109. package/dist/src/__tests__/integration/setup/helpers/fs-assertions.js.map +1 -0
  110. package/dist/src/__tests__/integration/setup/helpers/test-project.d.ts +38 -0
  111. package/dist/src/__tests__/integration/setup/helpers/test-project.d.ts.map +1 -0
  112. package/dist/src/__tests__/integration/setup/helpers/test-project.js +83 -0
  113. package/dist/src/__tests__/integration/setup/helpers/test-project.js.map +1 -0
  114. package/dist/src/__tests__/integration/setup/validation.test.d.ts +5 -0
  115. package/dist/src/__tests__/integration/setup/validation.test.d.ts.map +1 -0
  116. package/dist/src/__tests__/integration/setup/validation.test.js +318 -0
  117. package/dist/src/__tests__/integration/setup/validation.test.js.map +1 -0
  118. package/dist/src/cli.d.ts.map +1 -1
  119. package/dist/src/cli.js +20 -0
  120. package/dist/src/cli.js.map +1 -1
  121. package/dist/src/commands/setup-existing.d.ts +22 -0
  122. package/dist/src/commands/setup-existing.d.ts.map +1 -0
  123. package/dist/src/commands/setup-existing.js +408 -0
  124. package/dist/src/commands/setup-existing.js.map +1 -0
  125. package/dist/vitest.config.d.ts.map +1 -1
  126. package/dist/vitest.config.js +9 -6
  127. package/dist/vitest.config.js.map +1 -1
  128. package/docs/README.md +2 -2
  129. package/docs/contributing/development.md +37 -0
  130. package/docs/getting-started/{new-project-setup.md → new-repository-setup.md} +165 -38
  131. package/docs/getting-started/quick-start.md +57 -6
  132. package/docs/getting-started/setup.md +551 -180
  133. package/docs/guides/customization.md +4 -4
  134. package/docs/guides/multi-project.md +12 -9
  135. package/docs/reference/quick-reference.md +27 -18
  136. package/docs/testing/integration-tests.md +297 -0
  137. package/docs/testing-strategy.md +87 -0
  138. package/package.json +23 -6
  139. package/scripts/__tests__/create-project.test.ts +292 -0
  140. package/scripts/__tests__/multi-project-estimate.test.ts +145 -0
  141. package/scripts/__tests__/setup-existing-project.test.ts +147 -0
  142. package/scripts/__tests__/setup-interactive.test.ts +199 -0
  143. package/scripts/confluence-sync.ts +17 -29
  144. package/scripts/constants/__tests__/environments.test.ts +110 -0
  145. package/scripts/constants/__tests__/languages.test.ts +100 -0
  146. package/scripts/constants/environments.ts +61 -0
  147. package/scripts/constants/languages.ts +70 -0
  148. package/scripts/copy-static-assets.js +50 -0
  149. package/scripts/create-project.ts +251 -158
  150. package/scripts/jira-sync.ts +16 -1
  151. package/scripts/list-projects.ts +51 -24
  152. package/scripts/multi-project-estimate.ts +58 -22
  153. package/scripts/resource-dashboard.ts +91 -26
  154. package/scripts/setup-existing-project.ts +350 -230
  155. package/scripts/setup-existing.sh +159 -25
  156. package/scripts/setup-interactive.ts +565 -0
  157. package/scripts/template/__tests__/renderer.test.ts +207 -0
  158. package/scripts/template/renderer.ts +133 -0
  159. package/scripts/utils/__tests__/config-validator.test.ts +6 -0
  160. package/scripts/utils/__tests__/spec-updater.test.ts +220 -0
  161. package/scripts/utils/confluence-hierarchy.ts +7 -1
  162. package/scripts/utils/project-finder.ts +184 -0
  163. package/scripts/utils/spec-updater.ts +212 -0
  164. package/scripts/utils/template-finder.ts +75 -0
  165. package/templates/claude/commands/michi/confluence-sync.md +38 -0
  166. package/templates/claude/commands/michi/project-switch.md +36 -0
  167. package/templates/claude/rules/atlassian-integration.md +35 -0
  168. package/templates/claude/rules/michi-core.md +54 -0
  169. package/templates/claude-agent/README.md +25 -0
  170. package/templates/cursor/commands/michi/confluence-sync.md +76 -0
  171. package/templates/cursor/commands/michi/project-switch.md +69 -0
  172. package/templates/cursor/rules/atlassian-mcp.mdc +188 -0
  173. package/templates/cursor/rules/github-ssot.mdc +151 -0
  174. package/templates/cursor/rules/multi-project.mdc +81 -0
  175. 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 (error) {
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
- execSync(`jj git clone ${repoUrl} ${projectDir}`, { stdio: 'inherit' });
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 (error) {
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
- process.chdir(projectDir);
117
- console.log(` 📂 Working directory: ${projectDir}`);
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
- // Step 5: .kiro/project.json 作成
125
- console.log('\n📝 Step 5: Creating project metadata...');
126
- mkdirSync('.kiro', { recursive: true });
127
-
128
- // ラベル生成(安全なフォールバック付き)
129
- const labels = config.labels || (() => {
130
- // プロジェクトIDからプロジェクトラベル生成
131
- const projectLabel = repoName.toLowerCase().replace(/[^a-z0-9-]/g, '');
132
- const labelSet = new Set([`project:${projectLabel}`]);
133
-
134
- // ハイフンが存在する場合のみサービスラベルを生成
135
- if (repoName.includes('-')) {
136
- const parts = repoName.split('-');
137
- const servicePart = parts[parts.length - 1];
138
- const serviceLabel = servicePart.toLowerCase().replace(/[^a-z0-9-]/g, '');
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
- if (serviceLabel !== projectLabel) {
142
- labelSet.add(`service:${serviceLabel}`);
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
- return Array.from(labelSet);
147
- })();
148
-
149
- const projectJson = {
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}`);
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
- 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}`);
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
- // 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'
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 script of scriptsToCopy) {
230
- const src = join(scriptsDir, script);
231
- const dest = join(projectDir, 'scripts', script);
232
- if (existsSync(src)) {
233
- // ディレクトリが必要な場合は作成
234
- const destDir = dirname(dest);
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: scripts/${script}`);
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
- // package.json, tsconfig.json
243
- ['package.json', 'tsconfig.json'].forEach(file => {
244
- const src = join(michiPath, file);
245
- if (existsSync(src)) {
246
- cpSync(src, file);
247
- console.log(` ✅ Copied: ${file}`);
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
- // Step 7: .env テンプレート作成
252
- console.log('\n🔐 Step 7: Creating .env template...');
253
- execSync('npm run setup:env', { stdio: 'inherit' });
254
- console.log('.env created');
255
-
256
- // Step 8: npm install
257
- console.log('\n📦 Step 8: Installing dependencies...');
258
- execSync('npm install', { stdio: 'inherit' });
259
- console.log(' ✅ Dependencies installed');
260
-
261
- // Step 9: 初期コミット
262
- console.log('\n💾 Step 9: Creating initial commit...');
263
- execSync(`jj commit -m "chore: プロジェクト初期化
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連携設定"`, { stdio: 'inherit' });
269
-
270
- execSync('jj bookmark create main -r "@-"', { stdio: 'inherit' });
271
- console.log(' ✅ Initial commit created');
272
-
273
- // 完了メッセージ
274
- console.log('\n');
275
- console.log('🎉 プロジェクトセットアップ完了!');
276
- console.log('');
277
- console.log('次のステップ:');
278
- console.log(` 1. cd ${projectDir}`);
279
- console.log(' 2. .env ファイルを編集して認証情報を設定');
280
- console.log(' 3. jj git push --bookmark main --allow-new');
281
- console.log(' 4. Cursor で開く: cursor .');
282
- console.log(' 5. /kiro:spec-init <機能説明> で開発開始');
283
- console.log('');
284
- console.log('詳細: docs/new-project-setup.md');
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
  // 実行
@@ -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実行
@@ -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
- const { data: repos } = await octokit.repos.listForOrg({ org });
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
- // .kiro/project.json を取得
39
- const { data } = await octokit.repos.getContent({
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: '.kiro/project.json'
47
+ path: 'projects',
48
+ per_page: 100
43
49
  });
44
-
45
- if ('content' in data) {
46
- const content = Buffer.from(data.content, 'base64').toString('utf-8');
47
- const projectMeta = JSON.parse(content);
48
-
49
- projects.push({
50
- name: projectMeta.projectName,
51
- projectId: projectMeta.projectId,
52
- status: projectMeta.status,
53
- jiraKey: projectMeta.jiraProjectKey,
54
- team: projectMeta.team
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
- // .kiro/project.json が存在しない場合はスキップ
84
+ // projects/ディレクトリが存在しない、または API エラーの場合はスキップ
85
+ console.warn(`⚠️ Skipping ${repo.name}:`, error instanceof Error ? error.message : 'Unknown error');
59
86
  continue;
60
87
  }
61
88
  }