@sk8metal/michi-cli 0.0.8 → 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 (104) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +202 -9
  3. package/dist/scripts/__tests__/setup-existing-project.test.js +67 -5
  4. package/dist/scripts/__tests__/setup-existing-project.test.js.map +1 -1
  5. package/dist/scripts/constants/__tests__/environments.test.d.ts +2 -0
  6. package/dist/scripts/constants/__tests__/environments.test.d.ts.map +1 -0
  7. package/dist/scripts/constants/__tests__/environments.test.js +91 -0
  8. package/dist/scripts/constants/__tests__/environments.test.js.map +1 -0
  9. package/dist/scripts/constants/__tests__/languages.test.d.ts +2 -0
  10. package/dist/scripts/constants/__tests__/languages.test.d.ts.map +1 -0
  11. package/dist/scripts/constants/__tests__/languages.test.js +82 -0
  12. package/dist/scripts/constants/__tests__/languages.test.js.map +1 -0
  13. package/dist/scripts/constants/environments.d.ts +33 -0
  14. package/dist/scripts/constants/environments.d.ts.map +1 -0
  15. package/dist/scripts/constants/environments.js +49 -0
  16. package/dist/scripts/constants/environments.js.map +1 -0
  17. package/dist/scripts/constants/languages.d.ts +23 -0
  18. package/dist/scripts/constants/languages.d.ts.map +1 -0
  19. package/dist/scripts/constants/languages.js +53 -0
  20. package/dist/scripts/constants/languages.js.map +1 -0
  21. package/dist/scripts/create-project.d.ts +4 -0
  22. package/dist/scripts/create-project.d.ts.map +1 -1
  23. package/dist/scripts/create-project.js +51 -22
  24. package/dist/scripts/create-project.js.map +1 -1
  25. package/dist/scripts/setup-existing-project.d.ts +3 -1
  26. package/dist/scripts/setup-existing-project.d.ts.map +1 -1
  27. package/dist/scripts/setup-existing-project.js +110 -55
  28. package/dist/scripts/setup-existing-project.js.map +1 -1
  29. package/dist/scripts/template/__tests__/renderer.test.d.ts +2 -0
  30. package/dist/scripts/template/__tests__/renderer.test.d.ts.map +1 -0
  31. package/dist/scripts/template/__tests__/renderer.test.js +165 -0
  32. package/dist/scripts/template/__tests__/renderer.test.js.map +1 -0
  33. package/dist/scripts/template/renderer.d.ts +70 -0
  34. package/dist/scripts/template/renderer.d.ts.map +1 -0
  35. package/dist/scripts/template/renderer.js +99 -0
  36. package/dist/scripts/template/renderer.js.map +1 -0
  37. package/dist/scripts/utils/template-finder.d.ts +37 -0
  38. package/dist/scripts/utils/template-finder.d.ts.map +1 -0
  39. package/dist/scripts/utils/template-finder.js +63 -0
  40. package/dist/scripts/utils/template-finder.js.map +1 -0
  41. package/dist/src/__tests__/integration/setup/claude-agent.test.d.ts +5 -0
  42. package/dist/src/__tests__/integration/setup/claude-agent.test.d.ts.map +1 -0
  43. package/dist/src/__tests__/integration/setup/claude-agent.test.js +125 -0
  44. package/dist/src/__tests__/integration/setup/claude-agent.test.js.map +1 -0
  45. package/dist/src/__tests__/integration/setup/claude.test.d.ts +5 -0
  46. package/dist/src/__tests__/integration/setup/claude.test.d.ts.map +1 -0
  47. package/dist/src/__tests__/integration/setup/claude.test.js +111 -0
  48. package/dist/src/__tests__/integration/setup/claude.test.js.map +1 -0
  49. package/dist/src/__tests__/integration/setup/cursor.test.d.ts +5 -0
  50. package/dist/src/__tests__/integration/setup/cursor.test.d.ts.map +1 -0
  51. package/dist/src/__tests__/integration/setup/cursor.test.js +162 -0
  52. package/dist/src/__tests__/integration/setup/cursor.test.js.map +1 -0
  53. package/dist/src/__tests__/integration/setup/helpers/fs-assertions.d.ts +32 -0
  54. package/dist/src/__tests__/integration/setup/helpers/fs-assertions.d.ts.map +1 -0
  55. package/dist/src/__tests__/integration/setup/helpers/fs-assertions.js +72 -0
  56. package/dist/src/__tests__/integration/setup/helpers/fs-assertions.js.map +1 -0
  57. package/dist/src/__tests__/integration/setup/helpers/test-project.d.ts +38 -0
  58. package/dist/src/__tests__/integration/setup/helpers/test-project.d.ts.map +1 -0
  59. package/dist/src/__tests__/integration/setup/helpers/test-project.js +83 -0
  60. package/dist/src/__tests__/integration/setup/helpers/test-project.js.map +1 -0
  61. package/dist/src/__tests__/integration/setup/validation.test.d.ts +5 -0
  62. package/dist/src/__tests__/integration/setup/validation.test.d.ts.map +1 -0
  63. package/dist/src/__tests__/integration/setup/validation.test.js +318 -0
  64. package/dist/src/__tests__/integration/setup/validation.test.js.map +1 -0
  65. package/dist/src/cli.d.ts.map +1 -1
  66. package/dist/src/cli.js +20 -0
  67. package/dist/src/cli.js.map +1 -1
  68. package/dist/src/commands/setup-existing.d.ts +22 -0
  69. package/dist/src/commands/setup-existing.d.ts.map +1 -0
  70. package/dist/src/commands/setup-existing.js +408 -0
  71. package/dist/src/commands/setup-existing.js.map +1 -0
  72. package/dist/vitest.config.d.ts.map +1 -1
  73. package/dist/vitest.config.js +4 -3
  74. package/dist/vitest.config.js.map +1 -1
  75. package/docs/getting-started/new-repository-setup.md +108 -28
  76. package/docs/getting-started/quick-start.md +57 -6
  77. package/docs/getting-started/setup.md +250 -2
  78. package/docs/guides/customization.md +3 -3
  79. package/docs/guides/multi-project.md +1 -1
  80. package/docs/reference/quick-reference.md +25 -16
  81. package/docs/testing/integration-tests.md +297 -0
  82. package/package.json +7 -2
  83. package/scripts/__tests__/setup-existing-project.test.ts +73 -5
  84. package/scripts/constants/__tests__/environments.test.ts +110 -0
  85. package/scripts/constants/__tests__/languages.test.ts +100 -0
  86. package/scripts/constants/environments.ts +61 -0
  87. package/scripts/constants/languages.ts +70 -0
  88. package/scripts/create-project.ts +52 -22
  89. package/scripts/setup-existing-project.ts +136 -57
  90. package/scripts/setup-existing.sh +130 -3
  91. package/scripts/template/__tests__/renderer.test.ts +207 -0
  92. package/scripts/template/renderer.ts +133 -0
  93. package/scripts/utils/template-finder.ts +75 -0
  94. package/templates/claude/commands/michi/confluence-sync.md +38 -0
  95. package/templates/claude/commands/michi/project-switch.md +36 -0
  96. package/templates/claude/rules/atlassian-integration.md +35 -0
  97. package/templates/claude/rules/michi-core.md +54 -0
  98. package/templates/claude-agent/README.md +25 -0
  99. package/templates/cursor/commands/michi/confluence-sync.md +76 -0
  100. package/templates/cursor/commands/michi/project-switch.md +69 -0
  101. package/templates/cursor/rules/atlassian-mcp.mdc +188 -0
  102. package/templates/cursor/rules/github-ssot.mdc +151 -0
  103. package/templates/cursor/rules/multi-project.mdc +81 -0
  104. package/scripts/setup-env.sh +0 -52
@@ -0,0 +1,100 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ supportedLanguages,
4
+ DEV_GUIDELINES_MAP,
5
+ getDevGuidelines,
6
+ isSupportedLanguage,
7
+ type SupportedLanguage
8
+ } from '../languages.js';
9
+
10
+ describe('languages', () => {
11
+ describe('supportedLanguages', () => {
12
+ it('should contain 12 languages', () => {
13
+ expect(supportedLanguages).toHaveLength(12);
14
+ });
15
+
16
+ it('should include expected languages', () => {
17
+ expect(supportedLanguages).toContain('ja');
18
+ expect(supportedLanguages).toContain('en');
19
+ expect(supportedLanguages).toContain('zh-TW');
20
+ expect(supportedLanguages).toContain('zh');
21
+ expect(supportedLanguages).toContain('es');
22
+ expect(supportedLanguages).toContain('pt');
23
+ expect(supportedLanguages).toContain('de');
24
+ expect(supportedLanguages).toContain('fr');
25
+ expect(supportedLanguages).toContain('ru');
26
+ expect(supportedLanguages).toContain('it');
27
+ expect(supportedLanguages).toContain('ko');
28
+ expect(supportedLanguages).toContain('ar');
29
+ });
30
+ });
31
+
32
+ describe('DEV_GUIDELINES_MAP', () => {
33
+ it('should have entries for all supported languages', () => {
34
+ for (const lang of supportedLanguages) {
35
+ expect(DEV_GUIDELINES_MAP[lang]).toBeDefined();
36
+ expect(DEV_GUIDELINES_MAP[lang]).not.toBe('');
37
+ }
38
+ });
39
+
40
+ it('should have English guideline', () => {
41
+ expect(DEV_GUIDELINES_MAP.en).toBe('- Think in English, generate responses in English');
42
+ });
43
+
44
+ it('should have Japanese guideline with bilingual instruction', () => {
45
+ expect(DEV_GUIDELINES_MAP.ja).toContain('Think in English');
46
+ expect(DEV_GUIDELINES_MAP.ja).toContain('日本語');
47
+ });
48
+
49
+ it('should contain bilingual instructions', () => {
50
+ // Most languages should mention "Think in English" in some form
51
+ const languagesWithEnglishInstruction = ['ja', 'en', 'es', 'pt', 'de', 'fr', 'ru', 'it', 'ko', 'ar'];
52
+ for (const lang of languagesWithEnglishInstruction) {
53
+ expect(DEV_GUIDELINES_MAP[lang as SupportedLanguage]).toContain('Think in English');
54
+ }
55
+
56
+ // Chinese languages have their own format
57
+ expect(DEV_GUIDELINES_MAP['zh-TW']).toContain('以英文思考');
58
+ expect(DEV_GUIDELINES_MAP.zh).toContain('以英文思考');
59
+ });
60
+ });
61
+
62
+ describe('getDevGuidelines', () => {
63
+ it('should return guidelines for valid language', () => {
64
+ const guidelines = getDevGuidelines('ja');
65
+ expect(guidelines).toBe(DEV_GUIDELINES_MAP.ja);
66
+ });
67
+
68
+ it('should return guidelines for all supported languages', () => {
69
+ for (const lang of supportedLanguages) {
70
+ const guidelines = getDevGuidelines(lang);
71
+ expect(guidelines).toBeDefined();
72
+ expect(guidelines).toBe(DEV_GUIDELINES_MAP[lang]);
73
+ }
74
+ });
75
+ });
76
+
77
+ describe('isSupportedLanguage', () => {
78
+ it('should return true for supported languages', () => {
79
+ expect(isSupportedLanguage('ja')).toBe(true);
80
+ expect(isSupportedLanguage('en')).toBe(true);
81
+ expect(isSupportedLanguage('zh-TW')).toBe(true);
82
+ });
83
+
84
+ it('should return false for unsupported languages', () => {
85
+ expect(isSupportedLanguage('xx')).toBe(false);
86
+ expect(isSupportedLanguage('invalid')).toBe(false);
87
+ expect(isSupportedLanguage('')).toBe(false);
88
+ });
89
+
90
+ it('should work as type guard', () => {
91
+ const lang: string = 'ja';
92
+ if (isSupportedLanguage(lang)) {
93
+ // TypeScript should recognize lang as SupportedLanguage here
94
+ const guidelines: string = DEV_GUIDELINES_MAP[lang];
95
+ expect(guidelines).toBeDefined();
96
+ }
97
+ });
98
+ });
99
+ });
100
+
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Environment configuration mapping for cc-sdd environments
3
+ *
4
+ * Issue #37: 環境別コピー実装
5
+ */
6
+
7
+ export interface EnvironmentConfig {
8
+ rulesDir: string;
9
+ commandsDir: string;
10
+ templateSource: string;
11
+ }
12
+
13
+ export type Environment = 'claude' | 'claude-agent' | 'cursor';
14
+
15
+ export const ENV_CONFIG: Record<Environment, EnvironmentConfig> = {
16
+ 'claude': {
17
+ rulesDir: '.claude/rules',
18
+ commandsDir: '.claude/commands/kiro',
19
+ templateSource: 'claude'
20
+ },
21
+ 'claude-agent': {
22
+ rulesDir: '.claude/rules',
23
+ commandsDir: '.claude/commands/kiro',
24
+ templateSource: 'claude-agent'
25
+ },
26
+ 'cursor': {
27
+ rulesDir: '.cursor/rules',
28
+ commandsDir: '.cursor/commands/kiro',
29
+ templateSource: 'cursor'
30
+ }
31
+ };
32
+
33
+ /**
34
+ * Get environment configuration
35
+ *
36
+ * @param env - Environment name
37
+ * @returns Environment configuration
38
+ */
39
+ export const getEnvironmentConfig = (env: Environment): EnvironmentConfig => {
40
+ return ENV_CONFIG[env];
41
+ };
42
+
43
+ /**
44
+ * Validate if an environment is supported
45
+ *
46
+ * @param env - Environment name to validate
47
+ * @returns True if supported, false otherwise
48
+ */
49
+ export const isSupportedEnvironment = (env: string): env is Environment => {
50
+ return env in ENV_CONFIG;
51
+ };
52
+
53
+ /**
54
+ * Get all supported environments
55
+ *
56
+ * @returns Array of supported environment names
57
+ */
58
+ export const getSupportedEnvironments = (): Environment[] => {
59
+ return Object.keys(ENV_CONFIG) as Environment[];
60
+ };
61
+
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Supported languages and development guidelines map
3
+ *
4
+ * Issue #37: 環境別コピー実装
5
+ */
6
+
7
+ export const supportedLanguages = [
8
+ 'ja', 'en', 'zh-TW', 'zh', 'es', 'pt',
9
+ 'de', 'fr', 'ru', 'it', 'ko', 'ar'
10
+ ] as const;
11
+
12
+ export type SupportedLanguage = typeof supportedLanguages[number];
13
+
14
+ export const DEV_GUIDELINES_MAP: Record<SupportedLanguage, string> = {
15
+ en: '- Think in English, generate responses in English',
16
+
17
+ ja: '- Think in English, but generate responses in Japanese ' +
18
+ '(思考は英語、回答の生成は日本語で行うように)',
19
+
20
+ 'zh-TW': '- 以英文思考,但以繁體中文生成回應' +
21
+ '(Think in English, generate in Traditional Chinese)',
22
+
23
+ zh: '- 以英文思考,但以简体中文生成回复' +
24
+ '(Think in English, generate in Simplified Chinese)',
25
+
26
+ es: '- Think in English, generate responses in Spanish ' +
27
+ '(Piensa en inglés, genera respuestas en español)',
28
+
29
+ pt: '- Think in English, generate responses in Portuguese ' +
30
+ '(Pense em inglês, gere respostas em português)',
31
+
32
+ de: '- Think in English, generate responses in German ' +
33
+ '(Denke auf Englisch, formuliere Antworten auf Deutsch)',
34
+
35
+ fr: '- Think in English, generate responses in French ' +
36
+ '(Pensez en anglais, générez des réponses en français)',
37
+
38
+ ru: '- Think in English, generate responses in Russian ' +
39
+ '(Думай по-английски, отвечай по-русски)',
40
+
41
+ it: '- Think in English, generate responses in Italian ' +
42
+ '(Pensa in inglese, genera risposte in italiano)',
43
+
44
+ ko: '- Think in English, generate responses in Korean ' +
45
+ '(영어로 사고하고, 한국어로 응답을 생성하세요)',
46
+
47
+ ar: '- Think in English, generate responses in Arabic ' +
48
+ '(فكر بالإنجليزية وأجب بالعربية)',
49
+ };
50
+
51
+ /**
52
+ * Get development guidelines for a specific language
53
+ *
54
+ * @param lang - Language code
55
+ * @returns Development guidelines string
56
+ */
57
+ export const getDevGuidelines = (lang: SupportedLanguage): string => {
58
+ return DEV_GUIDELINES_MAP[lang];
59
+ };
60
+
61
+ /**
62
+ * Validate if a language code is supported
63
+ *
64
+ * @param lang - Language code to validate
65
+ * @returns True if supported, false otherwise
66
+ */
67
+ export const isSupportedLanguage = (lang: string): lang is SupportedLanguage => {
68
+ return (supportedLanguages as readonly string[]).includes(lang);
69
+ };
70
+
@@ -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);
@@ -203,46 +208,71 @@ async function createProject(config: ProjectConfig): Promise<void> {
203
208
  writeFileSync('.kiro/project.json', JSON.stringify(projectJson, null, 2));
204
209
  console.log(' ✅ project.json created');
205
210
 
206
- // Step 8: Michiから共通ファイルをコピー
207
- console.log('\n📋 Step 8: Copying common files from Michi...');
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)');
208
214
  const michiPath = resolve(__dirname, '..');
209
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);
230
+ }
231
+
210
232
  // コピー先ディレクトリを事前に作成(actualProjectDir 配下に作成)
211
233
  console.log(` 📂 Copying to: ${actualProjectDir}`);
212
234
  mkdirSync(join(actualProjectDir, '.cursor/rules'), { recursive: true });
213
- mkdirSync(join(actualProjectDir, '.cursor/commands/kiro'), { recursive: true });
235
+ mkdirSync(join(actualProjectDir, '.cursor/commands/michi'), { recursive: true });
214
236
  mkdirSync(join(actualProjectDir, '.kiro/steering'), { recursive: true });
215
237
  mkdirSync(join(actualProjectDir, '.kiro/settings/templates'), { recursive: true });
216
238
  mkdirSync(join(actualProjectDir, 'scripts/utils'), { recursive: true });
217
239
 
218
- // ルールファイル
240
+ // ルールファイル(templates/から)
219
241
  const rulesToCopy = [
220
- 'multi-project.mdc',
221
- 'github-ssot.mdc',
222
- 'atlassian-mcp.mdc'
242
+ 'rules/multi-project.mdc',
243
+ 'rules/github-ssot.mdc',
244
+ 'rules/atlassian-mcp.mdc'
223
245
  ];
224
246
 
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)) {
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) {
229
253
  cpSync(src, dest);
230
- console.log(` ✅ Copied: .cursor/rules/${rule}`);
254
+ console.log(` ✅ Copied: .cursor/rules/${fileName} (from templates/)`);
255
+ } else {
256
+ console.log(` ⚠️ ${fileName} not found in templates/`);
231
257
  }
232
258
  }
233
259
 
234
- // カスタムコマンド
260
+ // カスタムコマンド(templates/から)
235
261
  const commandsToCopy = [
236
- 'confluence-sync.md',
237
- 'project-switch.md'
262
+ 'commands/michi/confluence-sync.md',
263
+ 'commands/michi/project-switch.md'
238
264
  ];
239
265
 
240
- for (const cmd of commandsToCopy) {
241
- const src = join(michiPath, '.cursor/commands/kiro', cmd);
242
- const dest = join(actualProjectDir, '.cursor/commands/kiro', cmd);
243
- if (existsSync(src)) {
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) {
244
272
  cpSync(src, dest);
245
- console.log(` ✅ Copied: .cursor/commands/kiro/${cmd}`);
273
+ console.log(` ✅ Copied: .cursor/commands/michi/${fileName} (from templates/)`);
274
+ } else {
275
+ console.log(` ⚠️ ${fileName} not found in templates/`);
246
276
  }
247
277
  }
248
278
 
@@ -339,7 +369,7 @@ async function createProject(config: ProjectConfig): Promise<void> {
339
369
  console.log(' 4. Cursor で開く: cursor .');
340
370
  console.log(' 5. /kiro:spec-init <機能説明> で開発開始');
341
371
  console.log('');
342
- console.log('詳細: docs/new-repository-setup.md');
372
+ console.log('詳細: docs/getting-started/new-repository-setup.md');
343
373
 
344
374
  } finally {
345
375
  // 元の作業ディレクトリに戻る
@@ -7,19 +7,41 @@
7
7
  * npx tsx /path/to/michi/scripts/setup-existing-project.ts \
8
8
  * --michi-path /path/to/michi \
9
9
  * --project-name "既存プロジェクト" \
10
- * --jira-key "EXIST"
10
+ * --jira-key "EXIST" \
11
+ * --environment cursor \
12
+ * --lang ja
11
13
  */
12
14
 
13
- import { cpSync, existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
14
- import { resolve, join, basename } from 'path';
15
+ import { cpSync, existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, statSync } from 'fs';
16
+ import { resolve, join, basename, dirname } from 'path';
17
+ import { fileURLToPath } from 'url';
15
18
  import { execSync } from 'child_process';
16
19
  import { findRepositoryRoot } from './utils/project-finder.js';
20
+ import {
21
+ type Environment,
22
+ getEnvironmentConfig,
23
+ isSupportedEnvironment
24
+ } from './constants/environments.js';
25
+ import {
26
+ type SupportedLanguage,
27
+ isSupportedLanguage
28
+ } from './constants/languages.js';
29
+ import {
30
+ createTemplateContext,
31
+ renderTemplate
32
+ } from './template/renderer.js';
33
+
34
+ // ES module で __dirname を取得
35
+ const __filename = fileURLToPath(import.meta.url);
36
+ const __dirname = dirname(__filename);
17
37
 
18
38
  interface SetupConfig {
19
39
  michiPath: string; // Michiリポジトリのパス
20
40
  projectName: string; // プロジェクト表示名
21
41
  jiraKey: string; // JIRAプロジェクトキー
22
42
  labels?: string[]; // Confluenceラベル(オプション)
43
+ environment: Environment; // cc-sdd環境: 'claude' | 'claude-agent' | 'cursor'
44
+ langCode: SupportedLanguage; // 言語コード: 'ja'
23
45
  }
24
46
 
25
47
  function parseArgs(): SetupConfig {
@@ -40,6 +62,12 @@ function parseArgs(): SetupConfig {
40
62
  case 'jira-key':
41
63
  config.jiraKey = value;
42
64
  break;
65
+ case 'environment':
66
+ config.environment = value as Environment;
67
+ break;
68
+ case 'lang':
69
+ config.langCode = value as SupportedLanguage;
70
+ break;
43
71
  }
44
72
  }
45
73
 
@@ -47,17 +75,68 @@ function parseArgs(): SetupConfig {
47
75
  if (!config.michiPath) {
48
76
  config.michiPath = resolve(__dirname, '..');
49
77
  }
78
+ if (!config.environment) {
79
+ config.environment = 'claude';
80
+ }
81
+ if (!config.langCode) {
82
+ config.langCode = 'ja';
83
+ }
50
84
 
51
85
  // 必須フィールドチェック
52
86
  if (!config.projectName || !config.jiraKey) {
53
87
  console.error('Missing required parameters');
54
- console.error('Usage: tsx setup-existing-project.ts --project-name <name> --jira-key <key> [--michi-path <path>]');
88
+ console.error('Usage: tsx setup-existing-project.ts --project-name <name> --jira-key <key> [--michi-path <path>] [--environment <env>] [--lang <code>]');
89
+ process.exit(1);
90
+ }
91
+
92
+ // 環境バリデーション
93
+ if (!isSupportedEnvironment(config.environment)) {
94
+ console.error(`Unsupported environment: ${config.environment}`);
95
+ console.error('Supported environments: claude, claude-agent, cursor');
96
+ process.exit(1);
97
+ }
98
+
99
+ // 言語バリデーション
100
+ if (!isSupportedLanguage(config.langCode)) {
101
+ console.error(`Unsupported language: ${config.langCode}`);
102
+ console.error('Supported languages: ja, en, zh-TW, zh, es, pt, de, fr, ru, it, ko, ar');
55
103
  process.exit(1);
56
104
  }
57
105
 
58
106
  return config as SetupConfig;
59
107
  }
60
108
 
109
+ /**
110
+ * Copy and render templates recursively
111
+ *
112
+ * @param sourceDir - Source directory containing templates
113
+ * @param destDir - Destination directory for rendered files
114
+ * @param context - Template context for rendering
115
+ */
116
+ function copyAndRenderTemplates(
117
+ sourceDir: string,
118
+ destDir: string,
119
+ context: ReturnType<typeof createTemplateContext>
120
+ ): void {
121
+ const entries = readdirSync(sourceDir, { withFileTypes: true });
122
+
123
+ for (const entry of entries) {
124
+ const sourcePath = join(sourceDir, entry.name);
125
+ const destPath = join(destDir, entry.name);
126
+
127
+ if (entry.isDirectory()) {
128
+ // ディレクトリは再帰的にコピー
129
+ mkdirSync(destPath, { recursive: true });
130
+ copyAndRenderTemplates(sourcePath, destPath, context);
131
+ } else if (entry.isFile()) {
132
+ // ファイルはレンダリングしてコピー
133
+ const content = readFileSync(sourcePath, 'utf-8');
134
+ const rendered = renderTemplate(content, context);
135
+ writeFileSync(destPath, rendered, 'utf-8');
136
+ }
137
+ }
138
+ }
139
+
61
140
  async function setupExistingProject(config: SetupConfig): Promise<void> {
62
141
  const currentDir = process.cwd();
63
142
  const projectId = basename(currentDir);
@@ -66,6 +145,8 @@ async function setupExistingProject(config: SetupConfig): Promise<void> {
66
145
  console.log(` プロジェクト: ${config.projectName}`);
67
146
  console.log(` ディレクトリ: ${currentDir}`);
68
147
  console.log(` Michiパス: ${config.michiPath}`);
148
+ console.log(` 環境: ${config.environment}`);
149
+ console.log(` 言語: ${config.langCode}`);
69
150
  console.log('');
70
151
 
71
152
  // リポジトリルートを検出
@@ -151,6 +232,7 @@ async function setupExistingProject(config: SetupConfig): Promise<void> {
151
232
  const projectJson = {
152
233
  projectId,
153
234
  projectName: config.projectName,
235
+ language: config.langCode,
154
236
  jiraProjectKey: config.jiraKey,
155
237
  confluenceLabels: labels,
156
238
  status: 'active',
@@ -163,49 +245,46 @@ async function setupExistingProject(config: SetupConfig): Promise<void> {
163
245
  writeFileSync('.kiro/project.json', JSON.stringify(projectJson, null, 2));
164
246
  console.log(' ✅ project.json created');
165
247
 
166
- // Step 4: Michiから共通ルールをコピー
167
- console.log('\n📋 Step 4: Copying common rules from Michi...');
248
+ // Step 4: 環境別テンプレートのコピーとレンダリング
249
+ console.log('\n📋 Step 4: Copying and rendering templates from Michi...');
168
250
 
169
- // ディレクトリを事前に作成
170
- mkdirSync(join(projectDir, '.cursor/rules'), { recursive: true });
171
- mkdirSync(join(projectDir, '.cursor/commands/kiro'), { recursive: true });
251
+ const envConfig = getEnvironmentConfig(config.environment);
252
+ const templateContext = createTemplateContext(
253
+ config.langCode,
254
+ '.kiro',
255
+ envConfig.rulesDir.startsWith('.') ? envConfig.rulesDir.substring(1, envConfig.rulesDir.indexOf('/', 1)) : envConfig.rulesDir.split('/')[0]
256
+ );
172
257
 
173
- const rulesToCopy = [
174
- 'multi-project.mdc',
175
- 'github-ssot.mdc',
176
- 'atlassian-mcp.mdc'
177
- ];
258
+ // テンプレートソースディレクトリ
259
+ const templateSourceDir = join(config.michiPath, 'templates', envConfig.templateSource);
178
260
 
179
- for (const rule of rulesToCopy) {
180
- const src = join(config.michiPath, '.cursor/rules', rule);
181
- const dest = join(projectDir, '.cursor/rules', rule);
182
- if (existsSync(src)) {
183
- cpSync(src, dest);
184
- console.log(` ✅ ${rule}`);
185
- } else {
186
- console.log(` ⚠️ ${rule} not found in Michi`);
261
+ if (!existsSync(templateSourceDir)) {
262
+ console.log(` ⚠️ Template source not found: ${templateSourceDir}`);
263
+ console.log(' Skipping template copy');
264
+ } else {
265
+ // rulesディレクトリのコピーとレンダリング
266
+ const rulesTemplateDir = join(templateSourceDir, 'rules');
267
+ const rulesDestDir = join(projectDir, envConfig.rulesDir);
268
+
269
+ if (existsSync(rulesTemplateDir)) {
270
+ mkdirSync(rulesDestDir, { recursive: true });
271
+ copyAndRenderTemplates(rulesTemplateDir, rulesDestDir, templateContext);
272
+ console.log(` ✅ Rules copied and rendered to ${envConfig.rulesDir}`);
187
273
  }
188
- }
189
-
190
- // Step 5: カスタムコマンドをコピー
191
- console.log('\n🔧 Step 5: Copying custom commands...');
192
-
193
- const commandsToCopy = [
194
- 'confluence-sync.md',
195
- 'project-switch.md'
196
- ];
197
274
 
198
- for (const cmd of commandsToCopy) {
199
- const src = join(config.michiPath, '.cursor/commands/kiro', cmd);
200
- const dest = join(projectDir, '.cursor/commands/kiro', cmd);
201
- if (existsSync(src)) {
202
- cpSync(src, dest);
203
- console.log(` ✅ ${cmd}`);
275
+ // commandsディレクトリのコピーとレンダリング
276
+ const commandsTemplateDir = join(templateSourceDir, 'commands');
277
+ const commandsDestDir = join(projectDir, envConfig.commandsDir);
278
+
279
+ if (existsSync(commandsTemplateDir)) {
280
+ mkdirSync(commandsDestDir, { recursive: true });
281
+ copyAndRenderTemplates(commandsTemplateDir, commandsDestDir, templateContext);
282
+ console.log(` ✅ Commands copied and rendered to ${envConfig.commandsDir}`);
204
283
  }
205
284
  }
206
285
 
207
- // Step 6: Steeringテンプレートをコピー
208
- console.log('\n📚 Step 6: Copying steering templates...');
286
+ // Step 5: Steeringテンプレートをコピー
287
+ console.log('\n📚 Step 5: Copying steering templates...');
209
288
 
210
289
  const steeringDir = join(config.michiPath, '.kiro/steering');
211
290
  if (existsSync(steeringDir)) {
@@ -213,8 +292,8 @@ async function setupExistingProject(config: SetupConfig): Promise<void> {
213
292
  console.log(' ✅ product.md, tech.md, structure.md');
214
293
  }
215
294
 
216
- // Step 7: テンプレートをコピー
217
- console.log('\n📄 Step 7: Copying spec templates...');
295
+ // Step 6: テンプレートをコピー
296
+ console.log('\n📄 Step 6: Copying spec templates...');
218
297
 
219
298
  const templatesDir = join(config.michiPath, '.kiro/settings/templates');
220
299
  if (existsSync(templatesDir)) {
@@ -222,8 +301,8 @@ async function setupExistingProject(config: SetupConfig): Promise<void> {
222
301
  console.log(' ✅ requirements.md, design.md, tasks.md');
223
302
  }
224
303
 
225
- // Step 8: CLIツールのセットアップ案内
226
- console.log('\n⚙️ Step 8: Setting up Michi CLI...');
304
+ // Step 7: CLIツールのセットアップ案内
305
+ console.log('\n⚙️ Step 7: Setting up Michi CLI...');
227
306
  console.log(' ✅ Michi CLI setup complete!');
228
307
  console.log('');
229
308
  console.log(' 📋 使用方法:');
@@ -235,8 +314,8 @@ async function setupExistingProject(config: SetupConfig): Promise<void> {
235
314
  console.log(' npm install -g @sk8metal/michi-cli');
236
315
  console.log(' michi jira:sync <feature>');
237
316
 
238
- // Step 9: package.json と tsconfig.json をリポジトリルートにコピー
239
- console.log('\n📦 Step 9: Setting up package.json and TypeScript...');
317
+ // Step 8: package.json と tsconfig.json をリポジトリルートにコピー
318
+ console.log('\n📦 Step 8: Setting up package.json and TypeScript...');
240
319
 
241
320
  // 既存の package.json があるかチェック(リポジトリルート)
242
321
  const hasPackageJson = existsSync(join(repoRoot, 'package.json'));
@@ -279,8 +358,8 @@ async function setupExistingProject(config: SetupConfig): Promise<void> {
279
358
  console.log(' ℹ️ Existing tsconfig.json found (kept)');
280
359
  }
281
360
 
282
- // Step 10: .env テンプレート作成(プロジェクトディレクトリに)
283
- console.log('\n🔐 Step 10: Creating .env template...');
361
+ // Step 9: .env テンプレート作成(プロジェクトディレクトリに)
362
+ console.log('\n🔐 Step 9: Creating .env template...');
284
363
 
285
364
  const envTemplate = `# Atlassian設定(MCP + REST API共通)
286
365
  ATLASSIAN_URL=https://your-domain.atlassian.net
@@ -308,8 +387,8 @@ JIRA_PROJECT_KEYS=${config.jiraKey}
308
387
  console.log(' ℹ️ .env already exists (kept)');
309
388
  }
310
389
 
311
- // Step 11: README.md を更新(オプション、プロジェクトディレクトリに)
312
- console.log('\n📖 Step 11: Updating documentation...');
390
+ // Step 10: README.md を更新(オプション、プロジェクトディレクトリに)
391
+ console.log('\n📖 Step 10: Updating documentation...');
313
392
 
314
393
  const readmePath = join(projectDir, 'README.md');
315
394
  if (existsSync(readmePath)) {
@@ -351,8 +430,8 @@ npm run github:create-pr <branch> # PR作成
351
430
  }
352
431
  }
353
432
 
354
- // Step 12: .gitignore 更新(リポジトリルートに)
355
- console.log('\n🚫 Step 12: Updating .gitignore...');
433
+ // Step 11: .gitignore 更新(リポジトリルートに)
434
+ console.log('\n🚫 Step 11: Updating .gitignore...');
356
435
 
357
436
  const gitignoreEntries = [
358
437
  '# AI Development Workflow',
@@ -393,14 +472,15 @@ npm run github:create-pr <branch> # PR作成
393
472
  console.log(' 2. .env ファイルを編集して認証情報を設定');
394
473
  console.log(' 3. package.json が既存の場合、スクリプトを手動追加');
395
474
  console.log(' 4. npm install で依存関係をインストール(リポジトリルートで実行)');
396
- console.log(' 5. jj commit でセットアップをコミット');
397
- console.log(' 6. Cursor で開く: cursor .');
398
- console.log(' 7. /kiro:spec-init <機能説明> で開発開始');
475
+ console.log(` 5. cc-sddを導入: npx cc-sdd@latest --lang ${config.langCode} --${config.environment}`);
476
+ console.log(' 6. jj commit でセットアップをコミット');
477
+ console.log(' 7. Cursor で開く: cursor .');
478
+ console.log(' 8. /kiro:spec-init <機能説明> で開発開始');
399
479
  console.log('');
400
480
  console.log('作成されたファイル:');
401
481
  console.log(` - ${projectDir}/.kiro/project.json`);
402
- console.log(` - ${projectDir}/.cursor/rules/ (3ファイル)`);
403
- console.log(` - ${projectDir}/.cursor/commands/kiro/ (2ファイル)`);
482
+ console.log(` - ${projectDir}/${envConfig.rulesDir}/`);
483
+ console.log(` - ${projectDir}/${envConfig.commandsDir}/`);
404
484
  console.log(` - ${projectDir}/.kiro/steering/ (3ファイル)`);
405
485
  console.log(` - ${projectDir}/.kiro/settings/templates/ (3ファイル)`);
406
486
  console.log(` - ${repoRoot}/package.json (新規の場合)`);
@@ -419,4 +499,3 @@ setupExistingProject(config).catch(error => {
419
499
  console.error('❌ Error:', error.message);
420
500
  process.exit(1);
421
501
  });
422
-