@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.
- package/CHANGELOG.md +10 -0
- package/README.md +3 -10
- package/dist/scripts/constants/environments.d.ts +1 -1
- package/dist/scripts/constants/environments.d.ts.map +1 -1
- package/dist/scripts/constants/environments.js +0 -20
- package/dist/scripts/constants/environments.js.map +1 -1
- package/dist/scripts/jira-sync.d.ts.map +1 -1
- package/dist/scripts/jira-sync.js +32 -2
- package/dist/scripts/jira-sync.js.map +1 -1
- package/dist/scripts/utils/template-finder.d.ts +2 -2
- package/dist/scripts/utils/template-finder.d.ts.map +1 -1
- package/dist/scripts/utils/template-finder.js +3 -8
- package/dist/scripts/utils/template-finder.js.map +1 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +0 -8
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/init.d.ts +0 -4
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +6 -30
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/setup-existing.d.ts +2 -6
- package/dist/src/commands/setup-existing.d.ts.map +1 -1
- package/dist/src/commands/setup-existing.js +8 -142
- package/dist/src/commands/setup-existing.js.map +1 -1
- package/docs/getting-started/configuration.md +0 -29
- package/docs/getting-started/quick-start.md +2 -2
- package/docs/guides/atlassian-integration.md +27 -0
- package/docs/guides/workflow.md +1 -1
- package/docs/reference/cli.md +0 -4
- package/docs/troubleshooting.md +0 -45
- package/package.json +1 -3
- package/scripts/__tests__/create-project.test.ts +12 -12
- package/scripts/__tests__/setup-existing-project.test.ts +22 -22
- package/scripts/constants/__tests__/environments.test.ts +7 -50
- package/scripts/constants/environments.ts +1 -27
- package/scripts/jira-sync.ts +36 -2
- package/scripts/template/__tests__/renderer.test.ts +21 -21
- package/scripts/utils/template-finder.ts +5 -11
- package/docs/guides/ai-tools.md +0 -311
- package/templates/cline/rules/atlassian-integration.md +0 -36
- package/templates/cline/rules/michi-core.md +0 -56
- package/templates/codex/AGENTS.override.md +0 -277
- package/templates/codex/prompts/confluence-sync.md +0 -177
- package/templates/codex/rules/README.md +0 -210
- package/templates/cursor/commands/kiro/kiro-spec-impl.md +0 -244
- package/templates/cursor/commands/kiro/kiro-spec-tasks.md +0 -354
- package/templates/cursor/commands/michi/confluence-sync.md +0 -76
- package/templates/cursor/commands/michi/project-switch.md +0 -69
- package/templates/cursor/commands/michi/spec-tasks.md +0 -117
- package/templates/cursor/rules/atlassian-mcp.mdc +0 -188
- package/templates/cursor/rules/github-ssot.mdc +0 -151
- package/templates/cursor/rules/multi-project.mdc +0 -81
- package/templates/gemini/commands/README.md +0 -41
- package/templates/gemini/rules/GEMINI.md +0 -80
package/docs/troubleshooting.md
CHANGED
|
@@ -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.
|
|
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('.
|
|
20
|
+
it('.claude, .kiro, scripts が actualProjectDir にコピーされる', () => {
|
|
21
21
|
const projectDir = '/test/repo';
|
|
22
22
|
const actualProjectDir = '/test/repo/projects/test-project';
|
|
23
23
|
|
|
24
|
-
// .
|
|
25
|
-
const
|
|
26
|
-
expect(
|
|
27
|
-
expect(
|
|
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
|
-
// .
|
|
30
|
-
const
|
|
29
|
+
// .claude/commands のコピー先パスを検証
|
|
30
|
+
const claudeCommandsPath = join(
|
|
31
31
|
actualProjectDir,
|
|
32
|
-
'.
|
|
32
|
+
'.claude/commands',
|
|
33
33
|
'test.md',
|
|
34
34
|
);
|
|
35
|
-
expect(
|
|
36
|
-
'projects/test-project/.
|
|
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, '.
|
|
67
|
-
join(actualProjectDir, '.
|
|
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: '.
|
|
25
|
-
commandsDir: '.
|
|
26
|
-
templateSource: '
|
|
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: '.
|
|
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
|
-
// .
|
|
148
|
-
const rulesDir = join(projectDir, '.
|
|
146
|
+
|
|
147
|
+
// .claude/rules ディレクトリの作成
|
|
148
|
+
const rulesDir = join(projectDir, '.claude/rules');
|
|
149
149
|
mkdirSync(rulesDir, { recursive: true });
|
|
150
|
-
|
|
151
|
-
// .
|
|
152
|
-
const commandsDir = join(projectDir, '.
|
|
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('.
|
|
157
|
-
expect(commandsDir).toContain('.
|
|
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 = '.
|
|
181
|
-
const envConfigCommandsDir = '.
|
|
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: '.
|
|
221
|
-
commandsDir: '.
|
|
222
|
-
templateSource: '
|
|
220
|
+
rulesDir: '.claude/rules',
|
|
221
|
+
commandsDir: '.claude/commands',
|
|
222
|
+
templateSource: 'claude'
|
|
223
223
|
};
|
|
224
224
|
|
|
225
|
-
expect(envConfig.rulesDir).toBe('.
|
|
226
|
-
expect(envConfig.commandsDir).toBe('.
|
|
227
|
-
expect(envConfig.templateSource).toBe('
|
|
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: '.
|
|
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 = '
|
|
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(
|
|
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
|
/**
|
package/scripts/jira-sync.ts
CHANGED
|
@@ -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
|
|
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 =
|
|
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', '.
|
|
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('.
|
|
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', '.
|
|
24
|
+
const contextJa = createTemplateContext('ja', '.kiro', '.claude');
|
|
25
25
|
expect(contextJa.DEV_GUIDELINES).toContain('日本語');
|
|
26
26
|
|
|
27
|
-
const contextEn = createTemplateContext('en', '.kiro', '.
|
|
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', '.
|
|
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', '.
|
|
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', '.
|
|
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', '.
|
|
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', '.
|
|
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', '.
|
|
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', '.
|
|
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: .
|
|
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', '.
|
|
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', '.
|
|
129
|
+
const context = createTemplateContext('ja', '.kiro', '.claude');
|
|
130
130
|
const result = renderJsonTemplate(template, context);
|
|
131
131
|
|
|
132
|
-
expect(result).toEqual(['ja', '.kiro', '.
|
|
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', '.
|
|
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', '.
|
|
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', '.
|
|
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', '.
|
|
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: .
|
|
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', '.
|
|
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
|
-
*
|
|
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/
|
|
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
|
-
//
|
|
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
|
|