@sk8metal/michi-cli 0.11.0 → 0.13.0

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 (54) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +3 -10
  3. package/dist/scripts/constants/environments.d.ts +1 -1
  4. package/dist/scripts/constants/environments.d.ts.map +1 -1
  5. package/dist/scripts/constants/environments.js +0 -20
  6. package/dist/scripts/constants/environments.js.map +1 -1
  7. package/dist/scripts/jira-sync.d.ts.map +1 -1
  8. package/dist/scripts/jira-sync.js +32 -2
  9. package/dist/scripts/jira-sync.js.map +1 -1
  10. package/dist/scripts/utils/template-finder.d.ts +2 -2
  11. package/dist/scripts/utils/template-finder.d.ts.map +1 -1
  12. package/dist/scripts/utils/template-finder.js +3 -8
  13. package/dist/scripts/utils/template-finder.js.map +1 -1
  14. package/dist/src/cli.d.ts.map +1 -1
  15. package/dist/src/cli.js +0 -8
  16. package/dist/src/cli.js.map +1 -1
  17. package/dist/src/commands/init.d.ts +0 -4
  18. package/dist/src/commands/init.d.ts.map +1 -1
  19. package/dist/src/commands/init.js +6 -30
  20. package/dist/src/commands/init.js.map +1 -1
  21. package/dist/src/commands/setup-existing.d.ts +2 -6
  22. package/dist/src/commands/setup-existing.d.ts.map +1 -1
  23. package/dist/src/commands/setup-existing.js +8 -142
  24. package/dist/src/commands/setup-existing.js.map +1 -1
  25. package/docs/getting-started/configuration.md +0 -29
  26. package/docs/getting-started/quick-start.md +2 -2
  27. package/docs/guides/atlassian-integration.md +27 -0
  28. package/docs/guides/workflow.md +1 -1
  29. package/docs/reference/cli.md +0 -4
  30. package/docs/troubleshooting.md +0 -45
  31. package/package.json +1 -3
  32. package/scripts/__tests__/create-project.test.ts +12 -12
  33. package/scripts/__tests__/setup-existing-project.test.ts +22 -22
  34. package/scripts/constants/__tests__/environments.test.ts +7 -50
  35. package/scripts/constants/environments.ts +1 -27
  36. package/scripts/jira-sync.ts +36 -2
  37. package/scripts/template/__tests__/renderer.test.ts +21 -21
  38. package/scripts/utils/template-finder.ts +5 -11
  39. package/docs/guides/ai-tools.md +0 -311
  40. package/templates/cline/rules/atlassian-integration.md +0 -36
  41. package/templates/cline/rules/michi-core.md +0 -56
  42. package/templates/codex/AGENTS.override.md +0 -277
  43. package/templates/codex/prompts/confluence-sync.md +0 -177
  44. package/templates/codex/rules/README.md +0 -210
  45. package/templates/cursor/commands/kiro/kiro-spec-impl.md +0 -244
  46. package/templates/cursor/commands/kiro/kiro-spec-tasks.md +0 -354
  47. package/templates/cursor/commands/michi/confluence-sync.md +0 -76
  48. package/templates/cursor/commands/michi/project-switch.md +0 -69
  49. package/templates/cursor/commands/michi/spec-tasks.md +0 -117
  50. package/templates/cursor/rules/atlassian-mcp.mdc +0 -188
  51. package/templates/cursor/rules/github-ssot.mdc +0 -151
  52. package/templates/cursor/rules/multi-project.mdc +0 -81
  53. package/templates/gemini/commands/README.md +0 -41
  54. package/templates/gemini/rules/GEMINI.md +0 -80
@@ -317,51 +317,6 @@ tasks.mdはMichiワークフロー形式ではなくAI-DLC形式です。
317
317
  - テンプレートに従って `tasks.md` を修正
318
318
  - 再度フォーマット検証を実行
319
319
 
320
- ## MCP接続エラー
321
-
322
- ### MCPサーバーに接続できない(Cursor)
323
-
324
- **症状**:
325
- ```
326
- ❌ Failed to connect to MCP server: atlassian
327
- ```
328
-
329
- **原因**:
330
- - `mcp.json` の設定が間違っている
331
- - MCPサーバーがインストールされていない
332
- - 認証情報が間違っている
333
-
334
- **解決策**:
335
-
336
- 1. `mcp.json` の配置場所を確認
337
- - **Windows**: `%APPDATA%\Cursor\User\globalStorage\saoudrizwan.claude-dev\settings\mcp.json`
338
- - **macOS**: `~/Library/Application Support/Cursor/User/globalStorage/saoudrizwan.claude-dev/settings/mcp.json`
339
- - **Linux**: `~/.config/Cursor/User/globalStorage/saoudrizwan.claude-dev/settings/mcp.json`
340
-
341
- 2. `mcp.json` の内容を確認
342
- ```json
343
- {
344
- "mcpServers": {
345
- "atlassian": {
346
- "command": "npx",
347
- "args": ["-y", "@atlassian/mcp-server-atlassian"],
348
- "env": {
349
- "ATLASSIAN_URL": "https://your-domain.atlassian.net",
350
- "ATLASSIAN_EMAIL": "your-email@company.com",
351
- "ATLASSIAN_API_TOKEN": "your-token-here"
352
- }
353
- }
354
- }
355
- }
356
- ```
357
-
358
- 3. MCPサーバーを手動でテスト
359
- ```bash
360
- npx -y @atlassian/mcp-server-atlassian
361
- ```
362
-
363
- 4. Cursorを再起動
364
-
365
320
  ## レートリミット超過エラー
366
321
 
367
322
  **症状**:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sk8metal/michi-cli",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "Managed Intelligent Comprehensive Hub for Integration - AI-driven development workflow automation",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -25,7 +25,6 @@
25
25
  "atlassian",
26
26
  "spec-driven-development",
27
27
  "cc-sdd",
28
- "cursor",
29
28
  "github"
30
29
  ],
31
30
  "bin": {
@@ -46,7 +45,6 @@
46
45
  "build": "tsc && node scripts/copy-static-assets.js",
47
46
  "postbuild": "node scripts/set-permissions.js",
48
47
  "prepare": "husky",
49
- "michi:setup:cursor": "tsx src/cli.ts setup-existing --cursor",
50
48
  "michi:setup:claude": "tsx src/cli.ts setup-existing --claude",
51
49
  "michi:setup:claude-agent": "tsx src/cli.ts setup-existing --claude-agent",
52
50
  "confluence:sync": "tsx scripts/confluence-sync.ts",
@@ -17,23 +17,23 @@ describe('create-project.ts パス問題', () => {
17
17
  vi.clearAllMocks();
18
18
  });
19
19
 
20
- it('.cursor, .kiro, scripts が actualProjectDir にコピーされる', () => {
20
+ it('.claude, .kiro, scripts が actualProjectDir にコピーされる', () => {
21
21
  const projectDir = '/test/repo';
22
22
  const actualProjectDir = '/test/repo/projects/test-project';
23
23
 
24
- // .cursor/rules のコピー先パスを検証
25
- const cursorRulesPath = join(actualProjectDir, '.cursor/rules', 'test.mdc');
26
- expect(cursorRulesPath).toContain('projects/test-project/.cursor/rules');
27
- expect(cursorRulesPath).not.toContain(join(projectDir, '.cursor'));
24
+ // .claude/rules のコピー先パスを検証
25
+ const claudeRulesPath = join(actualProjectDir, '.claude/rules', 'test.mdc');
26
+ expect(claudeRulesPath).toContain('projects/test-project/.claude/rules');
27
+ expect(claudeRulesPath).not.toContain(join(projectDir, '.claude'));
28
28
 
29
- // .cursor/commands のコピー先パスを検証
30
- const cursorCommandsPath = join(
29
+ // .claude/commands のコピー先パスを検証
30
+ const claudeCommandsPath = join(
31
31
  actualProjectDir,
32
- '.cursor/commands/kiro',
32
+ '.claude/commands',
33
33
  'test.md',
34
34
  );
35
- expect(cursorCommandsPath).toContain(
36
- 'projects/test-project/.cursor/commands',
35
+ expect(claudeCommandsPath).toContain(
36
+ 'projects/test-project/.claude/commands',
37
37
  );
38
38
 
39
39
  // .kiro/steering のコピー先パスを検証
@@ -63,8 +63,8 @@ describe('create-project.ts パス問題', () => {
63
63
  const actualProjectDir = '/test/repo/projects/test-project';
64
64
 
65
65
  const directories = [
66
- join(actualProjectDir, '.cursor/rules'),
67
- join(actualProjectDir, '.cursor/commands/kiro'),
66
+ join(actualProjectDir, '.claude/rules'),
67
+ join(actualProjectDir, '.claude/commands'),
68
68
  join(actualProjectDir, '.kiro/steering'),
69
69
  join(actualProjectDir, '.kiro/settings/templates'),
70
70
  join(actualProjectDir, 'scripts/utils'),
@@ -21,9 +21,9 @@ vi.mock('../utils/project-finder.js', () => ({
21
21
  }));
22
22
  vi.mock('../constants/environments.js', () => ({
23
23
  getEnvironmentConfig: vi.fn(() => ({
24
- rulesDir: '.cursor/rules',
25
- commandsDir: '.cursor/commands/kiro',
26
- templateSource: 'cursor'
24
+ rulesDir: '.claude/rules',
25
+ commandsDir: '.claude/commands',
26
+ templateSource: 'claude'
27
27
  })),
28
28
  isSupportedEnvironment: vi.fn(() => true)
29
29
  }));
@@ -35,7 +35,7 @@ vi.mock('../template/renderer.js', () => ({
35
35
  LANG_CODE: 'ja',
36
36
  DEV_GUIDELINES: '- Think in English, but generate responses in Japanese',
37
37
  KIRO_DIR: '.kiro',
38
- AGENT_DIR: '.cursor'
38
+ AGENT_DIR: '.claude'
39
39
  })),
40
40
  renderTemplate: vi.fn((content: string) => content)
41
41
  }));
@@ -143,18 +143,18 @@ ATLASSIAN_API_TOKEN=existing_token
143
143
 
144
144
  it('ディレクトリがコピー前に作成される', () => {
145
145
  const projectDir = '/test/repo/projects/test-project';
146
-
147
- // .cursor/rules ディレクトリの作成
148
- const rulesDir = join(projectDir, '.cursor/rules');
146
+
147
+ // .claude/rules ディレクトリの作成
148
+ const rulesDir = join(projectDir, '.claude/rules');
149
149
  mkdirSync(rulesDir, { recursive: true });
150
-
151
- // .cursor/commands/kiro ディレクトリの作成
152
- const commandsDir = join(projectDir, '.cursor/commands/kiro');
150
+
151
+ // .claude/commands ディレクトリの作成
152
+ const commandsDir = join(projectDir, '.claude/commands');
153
153
  mkdirSync(commandsDir, { recursive: true });
154
-
154
+
155
155
  // ディレクトリが正しく作成されることを確認
156
- expect(rulesDir).toContain('.cursor/rules');
157
- expect(commandsDir).toContain('.cursor/commands/kiro');
156
+ expect(rulesDir).toContain('.claude/rules');
157
+ expect(commandsDir).toContain('.claude/commands');
158
158
  });
159
159
 
160
160
  it('パッケージ名が @sk8metal/michi-cli に統一されている', () => {
@@ -177,8 +177,8 @@ ATLASSIAN_API_TOKEN=existing_token
177
177
  it('完了メッセージに scripts/ ディレクトリが含まれていない', () => {
178
178
  const projectDir = '/test/repo/projects/test-project';
179
179
  const repoRoot = '/test/repo';
180
- const envConfigRulesDir = '.cursor/rules';
181
- const envConfigCommandsDir = '.cursor/commands/kiro';
180
+ const envConfigRulesDir = '.claude/rules';
181
+ const envConfigCommandsDir = '.claude/commands';
182
182
 
183
183
  // 実際に作成されるファイルのリスト(環境別)
184
184
  const createdFiles = [
@@ -217,14 +217,14 @@ ATLASSIAN_API_TOKEN=existing_token
217
217
 
218
218
  it('環境別のディレクトリマッピングが正しい', () => {
219
219
  const envConfig = {
220
- rulesDir: '.cursor/rules',
221
- commandsDir: '.cursor/commands/kiro',
222
- templateSource: 'cursor'
220
+ rulesDir: '.claude/rules',
221
+ commandsDir: '.claude/commands',
222
+ templateSource: 'claude'
223
223
  };
224
224
 
225
- expect(envConfig.rulesDir).toBe('.cursor/rules');
226
- expect(envConfig.commandsDir).toBe('.cursor/commands/kiro');
227
- expect(envConfig.templateSource).toBe('cursor');
225
+ expect(envConfig.rulesDir).toBe('.claude/rules');
226
+ expect(envConfig.commandsDir).toBe('.claude/commands');
227
+ expect(envConfig.templateSource).toBe('claude');
228
228
  });
229
229
 
230
230
  it('テンプレートコンテキストが正しく作成される', () => {
@@ -232,7 +232,7 @@ ATLASSIAN_API_TOKEN=existing_token
232
232
  LANG_CODE: 'ja',
233
233
  DEV_GUIDELINES: '- Think in English, but generate responses in Japanese',
234
234
  KIRO_DIR: '.kiro',
235
- AGENT_DIR: '.cursor'
235
+ AGENT_DIR: '.claude'
236
236
  };
237
237
 
238
238
  expect(context).toHaveProperty('LANG_CODE');
@@ -13,10 +13,6 @@ describe('environments', () => {
13
13
  it('should have entries for all environments', () => {
14
14
  expect(ENV_CONFIG.claude).toBeDefined();
15
15
  expect(ENV_CONFIG['claude-agent']).toBeDefined();
16
- expect(ENV_CONFIG.cursor).toBeDefined();
17
- expect(ENV_CONFIG.gemini).toBeDefined();
18
- expect(ENV_CONFIG.codex).toBeDefined();
19
- expect(ENV_CONFIG.cline).toBeDefined();
20
16
  });
21
17
 
22
18
  it('should have correct structure for claude', () => {
@@ -33,34 +29,6 @@ describe('environments', () => {
33
29
  expect(config.templateSource).toBe('claude-agent');
34
30
  });
35
31
 
36
- it('should have correct structure for cursor', () => {
37
- const config = ENV_CONFIG.cursor;
38
- expect(config.rulesDir).toBe('.cursor/rules');
39
- expect(config.commandsDir).toBe('.cursor/commands');
40
- expect(config.templateSource).toBe('cursor');
41
- });
42
-
43
- it('should have correct structure for gemini', () => {
44
- const config = ENV_CONFIG.gemini;
45
- expect(config.rulesDir).toBe('.gemini');
46
- expect(config.commandsDir).toBe('.gemini/extensions');
47
- expect(config.templateSource).toBe('gemini');
48
- });
49
-
50
- it('should have correct structure for codex', () => {
51
- const config = ENV_CONFIG.codex;
52
- expect(config.rulesDir).toBe('.codex/docs');
53
- expect(config.commandsDir).toBe('.codex/docs');
54
- expect(config.templateSource).toBe('codex');
55
- });
56
-
57
- it('should have correct structure for cline', () => {
58
- const config = ENV_CONFIG.cline;
59
- expect(config.rulesDir).toBe('.clinerules/rules');
60
- expect(config.commandsDir).toBe('.clinerules/commands');
61
- expect(config.templateSource).toBe('cline');
62
- });
63
-
64
32
  it('should have all required properties for each environment', () => {
65
33
  for (const env of Object.keys(ENV_CONFIG) as Environment[]) {
66
34
  const config = ENV_CONFIG[env];
@@ -81,14 +49,7 @@ describe('environments', () => {
81
49
  });
82
50
 
83
51
  it('should return config for all supported environments', () => {
84
- const environments: Environment[] = [
85
- 'claude',
86
- 'claude-agent',
87
- 'cursor',
88
- 'gemini',
89
- 'codex',
90
- 'cline',
91
- ];
52
+ const environments: Environment[] = ['claude', 'claude-agent'];
92
53
  for (const env of environments) {
93
54
  const config = getEnvironmentConfig(env);
94
55
  expect(config).toBeDefined();
@@ -101,20 +62,20 @@ describe('environments', () => {
101
62
  it('should return true for supported environments', () => {
102
63
  expect(isSupportedEnvironment('claude')).toBe(true);
103
64
  expect(isSupportedEnvironment('claude-agent')).toBe(true);
104
- expect(isSupportedEnvironment('cursor')).toBe(true);
105
- expect(isSupportedEnvironment('gemini')).toBe(true);
106
- expect(isSupportedEnvironment('codex')).toBe(true);
107
- expect(isSupportedEnvironment('cline')).toBe(true);
108
65
  });
109
66
 
110
67
  it('should return false for unsupported environments', () => {
111
68
  expect(isSupportedEnvironment('invalid')).toBe(false);
112
69
  expect(isSupportedEnvironment('vscode')).toBe(false);
113
70
  expect(isSupportedEnvironment('')).toBe(false);
71
+ expect(isSupportedEnvironment('cursor')).toBe(false);
72
+ expect(isSupportedEnvironment('gemini')).toBe(false);
73
+ expect(isSupportedEnvironment('codex')).toBe(false);
74
+ expect(isSupportedEnvironment('cline')).toBe(false);
114
75
  });
115
76
 
116
77
  it('should work as type guard', () => {
117
- const env: string = 'cursor';
78
+ const env: string = 'claude';
118
79
  if (isSupportedEnvironment(env)) {
119
80
  // TypeScript should recognize env as Environment here
120
81
  const config: EnvironmentConfig = ENV_CONFIG[env];
@@ -126,13 +87,9 @@ describe('environments', () => {
126
87
  describe('getSupportedEnvironments', () => {
127
88
  it('should return all supported environments', () => {
128
89
  const environments = getSupportedEnvironments();
129
- expect(environments).toHaveLength(6);
90
+ expect(environments).toHaveLength(2);
130
91
  expect(environments).toContain('claude');
131
92
  expect(environments).toContain('claude-agent');
132
- expect(environments).toContain('cursor');
133
- expect(environments).toContain('gemini');
134
- expect(environments).toContain('codex');
135
- expect(environments).toContain('cline');
136
93
  });
137
94
 
138
95
  it('should return array with correct type', () => {
@@ -10,13 +10,7 @@ export interface EnvironmentConfig {
10
10
  templateSource: string;
11
11
  }
12
12
 
13
- export type Environment =
14
- | 'claude'
15
- | 'claude-agent'
16
- | 'cursor'
17
- | 'gemini'
18
- | 'codex'
19
- | 'cline';
13
+ export type Environment = 'claude' | 'claude-agent';
20
14
 
21
15
  export const ENV_CONFIG: Record<Environment, EnvironmentConfig> = {
22
16
  claude: {
@@ -29,26 +23,6 @@ export const ENV_CONFIG: Record<Environment, EnvironmentConfig> = {
29
23
  commandsDir: '.claude/commands',
30
24
  templateSource: 'claude-agent',
31
25
  },
32
- cursor: {
33
- rulesDir: '.cursor/rules',
34
- commandsDir: '.cursor/commands',
35
- templateSource: 'cursor',
36
- },
37
- gemini: {
38
- rulesDir: '.gemini',
39
- commandsDir: '.gemini/extensions',
40
- templateSource: 'gemini',
41
- },
42
- codex: {
43
- rulesDir: '.codex/docs',
44
- commandsDir: '.codex/docs',
45
- templateSource: 'codex',
46
- },
47
- cline: {
48
- rulesDir: '.clinerules/rules',
49
- commandsDir: '.clinerules/commands',
50
- templateSource: 'cline',
51
- },
52
26
  };
53
27
 
54
28
  /**
@@ -680,6 +680,34 @@ class JIRAClient {
680
680
  }
681
681
  }
682
682
 
683
+ /**
684
+ * リポジトリ名(repo部分のみ)を取得
685
+ * @param repository リポジトリURL(例: https://github.com/sk8metalme/michi.git)
686
+ * @returns リポジトリ名(例: michi)
687
+ */
688
+ function extractRepoName(repository: string): string {
689
+ // owner/repo 形式を抽出
690
+ const match = repository.match(/github\.com[:/]([\w.-]+\/[\w.-]+)(\.git)?/);
691
+ if (!match) {
692
+ // フォールバック: repository 全体を使用
693
+ return 'repo';
694
+ }
695
+
696
+ const ownerRepo = match[1]; // 例: sk8metalme/michi
697
+ const parts = ownerRepo.split('/');
698
+ return parts[1] || parts[0]; // repo部分のみ返す
699
+ }
700
+
701
+ /**
702
+ * JIRAチケットのタイトルプレフィックスを生成
703
+ * @param repoName リポジトリ名
704
+ * @param featureName 機能名
705
+ * @returns プレフィックス文字列(例: [michi][user-auth])
706
+ */
707
+ function createTitlePrefix(repoName: string, featureName: string): string {
708
+ return `[${repoName}][${featureName}]`;
709
+ }
710
+
683
711
  /**
684
712
  * Phase行からフェーズラベルを検出
685
713
  * @param line Markdown行
@@ -867,7 +895,9 @@ async function getOrCreateEpic(
867
895
 
868
896
  // Epic作成
869
897
  console.log('Creating Epic...');
870
- const epicSummary = `[${featureName}] ${projectMeta.projectName}`;
898
+ const repoName = extractRepoName(projectMeta.repository);
899
+ const titlePrefix = createTitlePrefix(repoName, featureName);
900
+ const epicSummary = `${titlePrefix} ${projectMeta.projectName}`;
871
901
 
872
902
  // 同じタイトルのEpicがすでに存在するかJQLで検索
873
903
  const jql = `project = ${projectMeta.jiraProjectKey} AND issuetype = Epic AND summary ~ "${featureName}"`;
@@ -964,6 +994,10 @@ async function syncTasksToJIRA(featureName: string): Promise<void> {
964
994
  const config = getJIRAConfig();
965
995
  const client = new JIRAClient(config);
966
996
 
997
+ // リポジトリ名を取得(タイトルプレフィックス用)
998
+ const repoName = extractRepoName(projectMeta.repository);
999
+ const titlePrefix = createTitlePrefix(repoName, featureName);
1000
+
967
1001
  // StoryタイプのIDを取得
968
1002
  const storyIssueTypeId = await getStoryIssueTypeId(
969
1003
  appConfig,
@@ -1058,7 +1092,7 @@ async function syncTasksToJIRA(featureName: string): Promise<void> {
1058
1092
  if (!storyMatch) continue;
1059
1093
 
1060
1094
  const storyTitle = storyMatch[1];
1061
- const storySummary = `Story: ${storyTitle}`;
1095
+ const storySummary = `${titlePrefix} Story: ${storyTitle}`;
1062
1096
 
1063
1097
  // 既に同じタイトルのStoryが存在するかチェック
1064
1098
  if (existingStorySummaries.has(storySummary)) {
@@ -9,7 +9,7 @@ import {
9
9
  describe('renderer', () => {
10
10
  describe('createTemplateContext', () => {
11
11
  it('should create context with all required fields', () => {
12
- const context = createTemplateContext('ja', '.kiro', '.cursor');
12
+ const context = createTemplateContext('ja', '.kiro', '.claude');
13
13
 
14
14
  expect(context).toHaveProperty('LANG_CODE');
15
15
  expect(context).toHaveProperty('DEV_GUIDELINES');
@@ -17,14 +17,14 @@ describe('renderer', () => {
17
17
  expect(context).toHaveProperty('AGENT_DIR');
18
18
  expect(context.LANG_CODE).toBe('ja');
19
19
  expect(context.KIRO_DIR).toBe('.kiro');
20
- expect(context.AGENT_DIR).toBe('.cursor');
20
+ expect(context.AGENT_DIR).toBe('.claude');
21
21
  });
22
22
 
23
23
  it('should include language-specific guidelines', () => {
24
- const contextJa = createTemplateContext('ja', '.kiro', '.cursor');
24
+ const contextJa = createTemplateContext('ja', '.kiro', '.claude');
25
25
  expect(contextJa.DEV_GUIDELINES).toContain('日本語');
26
26
 
27
- const contextEn = createTemplateContext('en', '.kiro', '.cursor');
27
+ const contextEn = createTemplateContext('en', '.kiro', '.claude');
28
28
  expect(contextEn.DEV_GUIDELINES).toContain('English');
29
29
  });
30
30
  });
@@ -32,7 +32,7 @@ describe('renderer', () => {
32
32
  describe('renderTemplate', () => {
33
33
  it('should replace single placeholder', () => {
34
34
  const template = 'Language: {{LANG_CODE}}';
35
- const context = createTemplateContext('ja', '.kiro', '.cursor');
35
+ const context = createTemplateContext('ja', '.kiro', '.claude');
36
36
  const result = renderTemplate(template, context);
37
37
 
38
38
  expect(result).toBe('Language: ja');
@@ -48,7 +48,7 @@ describe('renderer', () => {
48
48
 
49
49
  it('should replace same placeholder multiple times', () => {
50
50
  const template = '{{LANG_CODE}} is {{LANG_CODE}}';
51
- const context = createTemplateContext('ja', '.kiro', '.cursor');
51
+ const context = createTemplateContext('ja', '.kiro', '.claude');
52
52
  const result = renderTemplate(template, context);
53
53
 
54
54
  expect(result).toBe('ja is ja');
@@ -56,7 +56,7 @@ describe('renderer', () => {
56
56
 
57
57
  it('should leave unknown placeholders unchanged', () => {
58
58
  const template = 'Known: {{LANG_CODE}}, Unknown: {{UNKNOWN}}';
59
- const context = createTemplateContext('ja', '.kiro', '.cursor');
59
+ const context = createTemplateContext('ja', '.kiro', '.claude');
60
60
  const result = renderTemplate(template, context);
61
61
 
62
62
  expect(result).toBe('Known: ja, Unknown: {{UNKNOWN}}');
@@ -64,7 +64,7 @@ describe('renderer', () => {
64
64
 
65
65
  it('should handle DEV_GUIDELINES placeholder', () => {
66
66
  const template = '{{DEV_GUIDELINES}}';
67
- const context = createTemplateContext('ja', '.kiro', '.cursor');
67
+ const context = createTemplateContext('ja', '.kiro', '.claude');
68
68
  const result = renderTemplate(template, context);
69
69
 
70
70
  expect(result).toContain('Think in English');
@@ -73,7 +73,7 @@ describe('renderer', () => {
73
73
 
74
74
  it('should handle empty template', () => {
75
75
  const template = '';
76
- const context = createTemplateContext('ja', '.kiro', '.cursor');
76
+ const context = createTemplateContext('ja', '.kiro', '.claude');
77
77
  const result = renderTemplate(template, context);
78
78
 
79
79
  expect(result).toBe('');
@@ -81,7 +81,7 @@ describe('renderer', () => {
81
81
 
82
82
  it('should handle template with no placeholders', () => {
83
83
  const template = 'No placeholders here';
84
- const context = createTemplateContext('ja', '.kiro', '.cursor');
84
+ const context = createTemplateContext('ja', '.kiro', '.claude');
85
85
  const result = renderTemplate(template, context);
86
86
 
87
87
  expect(result).toBe('No placeholders here');
@@ -91,19 +91,19 @@ describe('renderer', () => {
91
91
  const template = `Line 1: {{LANG_CODE}}
92
92
  Line 2: {{KIRO_DIR}}
93
93
  Line 3: {{AGENT_DIR}}`;
94
- const context = createTemplateContext('en', '.kiro', '.cursor');
94
+ const context = createTemplateContext('en', '.kiro', '.claude');
95
95
  const result = renderTemplate(template, context);
96
96
 
97
97
  expect(result).toBe(`Line 1: en
98
98
  Line 2: .kiro
99
- Line 3: .cursor`);
99
+ Line 3: .claude`);
100
100
  });
101
101
  });
102
102
 
103
103
  describe('renderJsonTemplate', () => {
104
104
  it('should render and parse JSON template', () => {
105
105
  const template = '{"lang": "{{LANG_CODE}}", "dir": "{{KIRO_DIR}}"}';
106
- const context = createTemplateContext('ja', '.kiro', '.cursor');
106
+ const context = createTemplateContext('ja', '.kiro', '.claude');
107
107
  const result = renderJsonTemplate(template, context);
108
108
 
109
109
  expect(result).toEqual({ lang: 'ja', dir: '.kiro' });
@@ -126,22 +126,22 @@ Line 3: .cursor`);
126
126
 
127
127
  it('should handle JSON arrays', () => {
128
128
  const template = '["{{LANG_CODE}}", "{{KIRO_DIR}}", "{{AGENT_DIR}}"]';
129
- const context = createTemplateContext('ja', '.kiro', '.cursor');
129
+ const context = createTemplateContext('ja', '.kiro', '.claude');
130
130
  const result = renderJsonTemplate(template, context);
131
131
 
132
- expect(result).toEqual(['ja', '.kiro', '.cursor']);
132
+ expect(result).toEqual(['ja', '.kiro', '.claude']);
133
133
  });
134
134
 
135
135
  it('should throw on invalid JSON', () => {
136
136
  const template = 'invalid json {{LANG_CODE}}';
137
- const context = createTemplateContext('ja', '.kiro', '.cursor');
137
+ const context = createTemplateContext('ja', '.kiro', '.claude');
138
138
 
139
139
  expect(() => renderJsonTemplate(template, context)).toThrow();
140
140
  });
141
141
 
142
142
  it('should throw with descriptive error for invalid JSON', () => {
143
143
  const template = '{"incomplete": {{LANG_CODE}}';
144
- const context = createTemplateContext('ja', '.kiro', '.cursor');
144
+ const context = createTemplateContext('ja', '.kiro', '.claude');
145
145
 
146
146
  try {
147
147
  renderJsonTemplate(template, context);
@@ -162,7 +162,7 @@ Line 3: .cursor`);
162
162
 
163
163
  it('should preserve original error stack', () => {
164
164
  const template = '{invalid json}';
165
- const context = createTemplateContext('en', '.kiro', '.cursor');
165
+ const context = createTemplateContext('en', '.kiro', '.claude');
166
166
 
167
167
  try {
168
168
  renderJsonTemplate(template, context);
@@ -184,19 +184,19 @@ Line 3: .cursor`);
184
184
  template2: 'Dir: {{KIRO_DIR}}',
185
185
  template3: 'Agent: {{AGENT_DIR}}'
186
186
  };
187
- const context = createTemplateContext('ja', '.kiro', '.cursor');
187
+ const context = createTemplateContext('ja', '.kiro', '.claude');
188
188
  const results = renderTemplates(templates, context);
189
189
 
190
190
  expect(results).toEqual({
191
191
  template1: 'Lang: ja',
192
192
  template2: 'Dir: .kiro',
193
- template3: 'Agent: .cursor'
193
+ template3: 'Agent: .claude'
194
194
  });
195
195
  });
196
196
 
197
197
  it('should handle empty templates object', () => {
198
198
  const templates = {};
199
- const context = createTemplateContext('ja', '.kiro', '.cursor');
199
+ const context = createTemplateContext('ja', '.kiro', '.claude');
200
200
  const results = renderTemplates(templates, context);
201
201
 
202
202
  expect(results).toEqual({});
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * テンプレートファイル検索ユーティリティ
3
- *
3
+ *
4
4
  * Issue #35: cc-sdd準拠のテンプレート検索
5
- * Cursor/Claude両環境のテンプレートを優先順位付きで検索
5
+ * Claude環境のテンプレートを検索
6
6
  */
7
7
 
8
8
  import { existsSync } from 'fs';
@@ -18,22 +18,16 @@ import { join } from 'path';
18
18
  * @example
19
19
  * ```typescript
20
20
  * const path = findTemplateFile('/path/to/michi', 'rules/github-ssot.mdc');
21
- * // returns: '/path/to/michi/templates/cursor/rules/github-ssot.mdc'
21
+ * // returns: '/path/to/michi/templates/claude/rules/github-ssot.mdc'
22
22
  * ```
23
23
  */
24
24
  export function findTemplateFile(michiPath: string, relativePath: string): string | null {
25
- // 優先順位1: templates/cursor/
26
- const cursorPath = join(michiPath, 'templates/cursor', relativePath);
27
- if (existsSync(cursorPath)) {
28
- return cursorPath;
29
- }
30
-
31
- // 優先順位2: templates/claude/
25
+ // templates/claude/
32
26
  const claudePath = join(michiPath, 'templates/claude', relativePath);
33
27
  if (existsSync(claudePath)) {
34
28
  return claudePath;
35
29
  }
36
-
30
+
37
31
  return null;
38
32
  }
39
33