@sk8metal/michi-cli 0.10.1 → 0.12.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/README.md +71 -848
- 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/phase-runner.js +1 -1
- package/dist/scripts/phase-runner.js.map +1 -1
- package/dist/scripts/utils/multi-repo-validator.d.ts +18 -0
- package/dist/scripts/utils/multi-repo-validator.d.ts.map +1 -1
- package/dist/scripts/utils/multi-repo-validator.js +42 -0
- package/dist/scripts/utils/multi-repo-validator.js.map +1 -1
- package/dist/scripts/utils/tasks-format-validator.js +3 -3
- package/dist/scripts/utils/tasks-format-validator.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/README.md +20 -83
- package/docs/getting-started/configuration.md +350 -0
- package/docs/getting-started/installation.md +59 -0
- package/docs/getting-started/quick-start.md +76 -0
- package/docs/guides/atlassian-integration.md +116 -0
- package/docs/guides/claude-code.md +155 -0
- package/docs/guides/multi-repo.md +117 -0
- package/docs/guides/workflow.md +382 -0
- package/docs/reference/ai-commands.md +92 -0
- package/docs/reference/cli.md +752 -0
- package/docs/reference/environment-variables.md +192 -0
- package/docs/troubleshooting.md +498 -0
- 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/phase-runner.ts +1 -1
- package/scripts/template/__tests__/renderer.test.ts +21 -21
- package/scripts/utils/__tests__/multi-repo-validator.test.ts +159 -1
- package/scripts/utils/multi-repo-validator.ts +50 -0
- package/scripts/utils/tasks-format-validator.ts +3 -3
- package/scripts/utils/template-finder.ts +5 -11
- package/templates/claude/agents/e2e-first-planner/AGENT.md +1 -1
- package/templates/claude/agents/pr-resolver/AGENT.md +15 -3
- package/templates/claude/commands/michi/e2e-plan.md +1 -1
- package/templates/claude/commands/michi/spec-design.md +2 -2
- package/templates/claude/commands/michi/spec-tasks.md +156 -0
- package/templates/claude/commands/michi/test-planning.md +1 -1
- package/templates/claude/commands/michi/validate-design.md +3 -3
- package/templates/claude/commands/michi-multi-repo/impl-all.md +30 -1
- package/templates/claude/commands/michi-multi-repo/propagate-specs.md +14 -1
- package/templates/claude/commands/michi-multi-repo/spec-review.md +16 -2
- package/templates/claude-agent/agents/repo-spec-executor.md +1 -1
- package/templates/claude-agent/commands/michi/spec-tasks.md +117 -0
- package/templates/claude-agent/rules/code-size-monitor.md +26 -0
- package/templates/claude-agent/rules/code-size-rules.md +32 -0
- package/templates/michi/cc-sdd-overrides/settings/rules/design-review-michi.md +1 -1
- package/docs/context.md +0 -59
- package/docs/michi-development/contributing/development.md +0 -341
- package/docs/michi-development/contributing/release.md +0 -365
- package/docs/michi-development/design/config-unification.md +0 -733
- package/docs/michi-development/design/design-config-current-state.md +0 -330
- package/docs/michi-development/design/design-config-implementation.md +0 -628
- package/docs/michi-development/design/design-config-migration.md +0 -952
- package/docs/michi-development/design/design-config-security.md +0 -771
- package/docs/michi-development/design/design-config-solution.md +0 -583
- package/docs/michi-development/design/design-config-testing.md +0 -892
- package/docs/michi-development/testing/manual-verification-flow.md +0 -871
- package/docs/michi-development/testing/manual-verification-other-tools.md +0 -1279
- package/docs/michi-development/testing/manual-verification-troubleshooting.md +0 -122
- package/docs/michi-development/testing/pre-publish-checklist.md +0 -560
- package/docs/michi-development/testing-strategy.md +0 -87
- package/docs/plan.md +0 -275
- package/docs/user-guide/getting-started/github-token-setup.md +0 -510
- package/docs/user-guide/getting-started/new-repository-setup.md +0 -704
- package/docs/user-guide/getting-started/quick-start.md +0 -212
- package/docs/user-guide/getting-started/setup.md +0 -819
- package/docs/user-guide/guides/agent-skills-integration.md +0 -222
- package/docs/user-guide/guides/customization.md +0 -537
- package/docs/user-guide/guides/internationalization.md +0 -540
- package/docs/user-guide/guides/migration-guide.md +0 -138
- package/docs/user-guide/guides/multi-project.md +0 -368
- package/docs/user-guide/guides/multi-repo-guide.md +0 -1590
- package/docs/user-guide/guides/phase-automation.md +0 -419
- package/docs/user-guide/guides/workflow.md +0 -574
- package/docs/user-guide/hands-on/README.md +0 -142
- package/docs/user-guide/hands-on/claude-agent-setup.md +0 -597
- package/docs/user-guide/hands-on/claude-setup.md +0 -452
- package/docs/user-guide/hands-on/cursor-setup.md +0 -353
- package/docs/user-guide/hands-on/troubleshooting.md +0 -964
- package/docs/user-guide/hands-on/verification-checklist.md +0 -439
- package/docs/user-guide/hands-on/workflow-walkthrough.md +0 -1078
- package/docs/user-guide/reference/config.md +0 -589
- package/docs/user-guide/reference/multi-repo-api.md +0 -771
- package/docs/user-guide/reference/quick-reference.md +0 -297
- package/docs/user-guide/reference/security-test-payloads.md +0 -50
- package/docs/user-guide/reference/tasks-template.md +0 -550
- package/docs/user-guide/release/ci-setup-java.md +0 -114
- package/docs/user-guide/release/ci-setup-nodejs.md +0 -94
- package/docs/user-guide/release/ci-setup-php.md +0 -102
- package/docs/user-guide/release/ci-setup-troubleshooting.md +0 -94
- package/docs/user-guide/release/ci-setup.md +0 -188
- package/docs/user-guide/release/release-flow.md +0 -476
- package/docs/user-guide/templates/test-specs/README.md +0 -173
- package/docs/user-guide/templates/test-specs/e2e-test-spec-template.md +0 -553
- package/docs/user-guide/templates/test-specs/integration-test-spec-template.md +0 -435
- package/docs/user-guide/templates/test-specs/performance-test-spec-template.md +0 -454
- package/docs/user-guide/templates/test-specs/security-test-spec-template.md +0 -625
- package/docs/user-guide/templates/test-specs/unit-test-spec-template.md +0 -328
- package/docs/user-guide/testing/integration-tests.md +0 -312
- package/docs/user-guide/testing/tdd-cycle.md +0 -349
- package/docs/user-guide/testing/test-execution-flow.md +0 -396
- package/docs/user-guide/testing/test-failure-handling.md +0 -521
- package/docs/user-guide/testing/test-planning-flow.md +0 -185
- package/docs/user-guide/testing-strategy.md +0 -185
- package/docs/verification-guide.md +0 -518
- 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/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
|
@@ -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/phase-runner.ts
CHANGED
|
@@ -229,7 +229,7 @@ async function checkTasksPrerequisites(
|
|
|
229
229
|
const tasksPath = join(process.cwd(), '.kiro', 'specs', feature, 'tasks.md');
|
|
230
230
|
if (!existsSync(tasksPath)) {
|
|
231
231
|
errors.push(
|
|
232
|
-
'tasks.mdが存在しません。先に/
|
|
232
|
+
'tasks.mdが存在しません。先に/michi:spec-tasks を実行してください',
|
|
233
233
|
);
|
|
234
234
|
return { valid: false, errors };
|
|
235
235
|
}
|
|
@@ -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({});
|
|
@@ -3,12 +3,19 @@
|
|
|
3
3
|
* Task 1.2: バリデーション関数の実装
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
7
7
|
import {
|
|
8
8
|
validateProjectName,
|
|
9
9
|
validateJiraKey,
|
|
10
10
|
validateRepositoryUrl,
|
|
11
|
+
hasMichiSetup,
|
|
12
|
+
getMichiSetupCommand,
|
|
13
|
+
validateLocalPath,
|
|
11
14
|
} from '../multi-repo-validator';
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import * as os from 'os';
|
|
18
|
+
import type { Repository } from '../../config/config-schema';
|
|
12
19
|
|
|
13
20
|
describe('validateProjectName', () => {
|
|
14
21
|
describe('正常ケース', () => {
|
|
@@ -333,3 +340,154 @@ describe('validateRepositoryUrl', () => {
|
|
|
333
340
|
});
|
|
334
341
|
});
|
|
335
342
|
});
|
|
343
|
+
|
|
344
|
+
describe('hasMichiSetup', () => {
|
|
345
|
+
let tempDir: string;
|
|
346
|
+
|
|
347
|
+
beforeEach(() => {
|
|
348
|
+
// 一時ディレクトリを作成
|
|
349
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'michi-test-'));
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
afterEach(() => {
|
|
353
|
+
// 一時ディレクトリを削除
|
|
354
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
describe('Michi導入済みの場合', () => {
|
|
358
|
+
it('.kiro/project.json が存在する場合はtrueを返す', () => {
|
|
359
|
+
// .kiro ディレクトリを作成
|
|
360
|
+
const kiroDir = path.join(tempDir, '.kiro');
|
|
361
|
+
fs.mkdirSync(kiroDir);
|
|
362
|
+
|
|
363
|
+
// project.json を作成
|
|
364
|
+
const projectJson = path.join(kiroDir, 'project.json');
|
|
365
|
+
fs.writeFileSync(projectJson, '{}');
|
|
366
|
+
|
|
367
|
+
const result = hasMichiSetup(tempDir);
|
|
368
|
+
expect(result).toBe(true);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
describe('Michi未導入の場合', () => {
|
|
373
|
+
it('.kiro/project.json が存在しない場合はfalseを返す', () => {
|
|
374
|
+
// .kiro ディレクトリのみ作成(project.jsonなし)
|
|
375
|
+
const kiroDir = path.join(tempDir, '.kiro');
|
|
376
|
+
fs.mkdirSync(kiroDir);
|
|
377
|
+
|
|
378
|
+
const result = hasMichiSetup(tempDir);
|
|
379
|
+
expect(result).toBe(false);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('.kiro ディレクトリ自体が存在しない場合はfalseを返す', () => {
|
|
383
|
+
// .kiro ディレクトリを作成しない
|
|
384
|
+
const result = hasMichiSetup(tempDir);
|
|
385
|
+
expect(result).toBe(false);
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
describe('getMichiSetupCommand', () => {
|
|
391
|
+
it('正しいセットアップコマンドを生成する(シングルクォートでラップ)', () => {
|
|
392
|
+
const localPath = '/path/to/repo';
|
|
393
|
+
const command = getMichiSetupCommand(localPath);
|
|
394
|
+
expect(command).toBe(
|
|
395
|
+
"cd '/path/to/repo' && npx @sk8metal/michi-cli@latest init",
|
|
396
|
+
);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('スペースを含むパスを正しくエスケープする', () => {
|
|
400
|
+
const localPath = '/path/to/my repo';
|
|
401
|
+
const command = getMichiSetupCommand(localPath);
|
|
402
|
+
expect(command).toBe(
|
|
403
|
+
"cd '/path/to/my repo' && npx @sk8metal/michi-cli@latest init",
|
|
404
|
+
);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('シングルクォートを含むパスを正しくエスケープする', () => {
|
|
408
|
+
const localPath = "/path/to/user's repo";
|
|
409
|
+
const command = getMichiSetupCommand(localPath);
|
|
410
|
+
expect(command).toBe(
|
|
411
|
+
"cd '/path/to/user'\\''s repo' && npx @sk8metal/michi-cli@latest init",
|
|
412
|
+
);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it('複数のシングルクォートを含むパスを正しくエスケープする', () => {
|
|
416
|
+
const localPath = "/path/to/'test'/repo's/dir";
|
|
417
|
+
const command = getMichiSetupCommand(localPath);
|
|
418
|
+
expect(command).toBe(
|
|
419
|
+
"cd '/path/to/'\\''test'\\''/repo'\\''s/dir' && npx @sk8metal/michi-cli@latest init",
|
|
420
|
+
);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('スペースとシングルクォート両方を含むパスを正しくエスケープする', () => {
|
|
424
|
+
const localPath = "/path/to/user's test repo";
|
|
425
|
+
const command = getMichiSetupCommand(localPath);
|
|
426
|
+
expect(command).toBe(
|
|
427
|
+
"cd '/path/to/user'\\''s test repo' && npx @sk8metal/michi-cli@latest init",
|
|
428
|
+
);
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
describe('validateLocalPath - Michi導入チェック', () => {
|
|
433
|
+
let tempDir: string;
|
|
434
|
+
let repository: Repository;
|
|
435
|
+
|
|
436
|
+
beforeEach(() => {
|
|
437
|
+
// 一時ディレクトリを作成
|
|
438
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'michi-test-'));
|
|
439
|
+
|
|
440
|
+
// リポジトリオブジェクトを作成
|
|
441
|
+
repository = {
|
|
442
|
+
name: 'test-repo',
|
|
443
|
+
url: 'https://github.com/test/repo',
|
|
444
|
+
branch: 'main',
|
|
445
|
+
localPath: tempDir,
|
|
446
|
+
};
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
afterEach(() => {
|
|
450
|
+
// 一時ディレクトリを削除
|
|
451
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it('Michi導入済みリポジトリの場合、hasMichiSetupがtrueになる', () => {
|
|
455
|
+
// .git ディレクトリを作成(Gitリポジトリとして認識させる)
|
|
456
|
+
fs.mkdirSync(path.join(tempDir, '.git'));
|
|
457
|
+
|
|
458
|
+
// .kiro/project.json を作成(Michi導入済み)
|
|
459
|
+
const kiroDir = path.join(tempDir, '.kiro');
|
|
460
|
+
fs.mkdirSync(kiroDir);
|
|
461
|
+
fs.writeFileSync(path.join(kiroDir, 'project.json'), '{}');
|
|
462
|
+
|
|
463
|
+
const result = validateLocalPath(repository);
|
|
464
|
+
expect(result.hasMichiSetup).toBe(true);
|
|
465
|
+
expect(result.michiSetupCommand).toBeNull();
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it('Michi未導入リポジトリの場合、hasMichiSetupがfalseになり警告が追加される', () => {
|
|
469
|
+
// .git ディレクトリを作成(Gitリポジトリとして認識させる)
|
|
470
|
+
fs.mkdirSync(path.join(tempDir, '.git'));
|
|
471
|
+
|
|
472
|
+
// .kiro/project.json は作成しない(Michi未導入)
|
|
473
|
+
|
|
474
|
+
const result = validateLocalPath(repository);
|
|
475
|
+
expect(result.hasMichiSetup).toBe(false);
|
|
476
|
+
expect(result.michiSetupCommand).not.toBeNull();
|
|
477
|
+
expect(result.warnings).toContain(
|
|
478
|
+
`Repository 'test-repo' does not have Michi setup (.kiro/project.json not found)`,
|
|
479
|
+
);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it('Gitリポジトリでない場合、hasMichiSetupはfalseでmichiSetupCommandはnull', () => {
|
|
483
|
+
// .git ディレクトリを作成しない(Gitリポジトリでない)
|
|
484
|
+
|
|
485
|
+
const result = validateLocalPath(repository);
|
|
486
|
+
expect(result.isValid).toBe(false);
|
|
487
|
+
expect(result.hasMichiSetup).toBe(false);
|
|
488
|
+
expect(result.michiSetupCommand).toBeNull();
|
|
489
|
+
expect(result.errors).toContain(
|
|
490
|
+
`localPath '${tempDir}' is not a Git repository (no .git directory)`,
|
|
491
|
+
);
|
|
492
|
+
});
|
|
493
|
+
});
|