@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.
Files changed (139) hide show
  1. package/README.md +71 -848
  2. package/dist/scripts/constants/environments.d.ts +1 -1
  3. package/dist/scripts/constants/environments.d.ts.map +1 -1
  4. package/dist/scripts/constants/environments.js +0 -20
  5. package/dist/scripts/constants/environments.js.map +1 -1
  6. package/dist/scripts/phase-runner.js +1 -1
  7. package/dist/scripts/phase-runner.js.map +1 -1
  8. package/dist/scripts/utils/multi-repo-validator.d.ts +18 -0
  9. package/dist/scripts/utils/multi-repo-validator.d.ts.map +1 -1
  10. package/dist/scripts/utils/multi-repo-validator.js +42 -0
  11. package/dist/scripts/utils/multi-repo-validator.js.map +1 -1
  12. package/dist/scripts/utils/tasks-format-validator.js +3 -3
  13. package/dist/scripts/utils/tasks-format-validator.js.map +1 -1
  14. package/dist/scripts/utils/template-finder.d.ts +2 -2
  15. package/dist/scripts/utils/template-finder.d.ts.map +1 -1
  16. package/dist/scripts/utils/template-finder.js +3 -8
  17. package/dist/scripts/utils/template-finder.js.map +1 -1
  18. package/dist/src/cli.d.ts.map +1 -1
  19. package/dist/src/cli.js +0 -8
  20. package/dist/src/cli.js.map +1 -1
  21. package/dist/src/commands/init.d.ts +0 -4
  22. package/dist/src/commands/init.d.ts.map +1 -1
  23. package/dist/src/commands/init.js +6 -30
  24. package/dist/src/commands/init.js.map +1 -1
  25. package/dist/src/commands/setup-existing.d.ts +2 -6
  26. package/dist/src/commands/setup-existing.d.ts.map +1 -1
  27. package/dist/src/commands/setup-existing.js +8 -142
  28. package/dist/src/commands/setup-existing.js.map +1 -1
  29. package/docs/README.md +20 -83
  30. package/docs/getting-started/configuration.md +350 -0
  31. package/docs/getting-started/installation.md +59 -0
  32. package/docs/getting-started/quick-start.md +76 -0
  33. package/docs/guides/atlassian-integration.md +116 -0
  34. package/docs/guides/claude-code.md +155 -0
  35. package/docs/guides/multi-repo.md +117 -0
  36. package/docs/guides/workflow.md +382 -0
  37. package/docs/reference/ai-commands.md +92 -0
  38. package/docs/reference/cli.md +752 -0
  39. package/docs/reference/environment-variables.md +192 -0
  40. package/docs/troubleshooting.md +498 -0
  41. package/package.json +1 -3
  42. package/scripts/__tests__/create-project.test.ts +12 -12
  43. package/scripts/__tests__/setup-existing-project.test.ts +22 -22
  44. package/scripts/constants/__tests__/environments.test.ts +7 -50
  45. package/scripts/constants/environments.ts +1 -27
  46. package/scripts/phase-runner.ts +1 -1
  47. package/scripts/template/__tests__/renderer.test.ts +21 -21
  48. package/scripts/utils/__tests__/multi-repo-validator.test.ts +159 -1
  49. package/scripts/utils/multi-repo-validator.ts +50 -0
  50. package/scripts/utils/tasks-format-validator.ts +3 -3
  51. package/scripts/utils/template-finder.ts +5 -11
  52. package/templates/claude/agents/e2e-first-planner/AGENT.md +1 -1
  53. package/templates/claude/agents/pr-resolver/AGENT.md +15 -3
  54. package/templates/claude/commands/michi/e2e-plan.md +1 -1
  55. package/templates/claude/commands/michi/spec-design.md +2 -2
  56. package/templates/claude/commands/michi/spec-tasks.md +156 -0
  57. package/templates/claude/commands/michi/test-planning.md +1 -1
  58. package/templates/claude/commands/michi/validate-design.md +3 -3
  59. package/templates/claude/commands/michi-multi-repo/impl-all.md +30 -1
  60. package/templates/claude/commands/michi-multi-repo/propagate-specs.md +14 -1
  61. package/templates/claude/commands/michi-multi-repo/spec-review.md +16 -2
  62. package/templates/claude-agent/agents/repo-spec-executor.md +1 -1
  63. package/templates/claude-agent/commands/michi/spec-tasks.md +117 -0
  64. package/templates/claude-agent/rules/code-size-monitor.md +26 -0
  65. package/templates/claude-agent/rules/code-size-rules.md +32 -0
  66. package/templates/michi/cc-sdd-overrides/settings/rules/design-review-michi.md +1 -1
  67. package/docs/context.md +0 -59
  68. package/docs/michi-development/contributing/development.md +0 -341
  69. package/docs/michi-development/contributing/release.md +0 -365
  70. package/docs/michi-development/design/config-unification.md +0 -733
  71. package/docs/michi-development/design/design-config-current-state.md +0 -330
  72. package/docs/michi-development/design/design-config-implementation.md +0 -628
  73. package/docs/michi-development/design/design-config-migration.md +0 -952
  74. package/docs/michi-development/design/design-config-security.md +0 -771
  75. package/docs/michi-development/design/design-config-solution.md +0 -583
  76. package/docs/michi-development/design/design-config-testing.md +0 -892
  77. package/docs/michi-development/testing/manual-verification-flow.md +0 -871
  78. package/docs/michi-development/testing/manual-verification-other-tools.md +0 -1279
  79. package/docs/michi-development/testing/manual-verification-troubleshooting.md +0 -122
  80. package/docs/michi-development/testing/pre-publish-checklist.md +0 -560
  81. package/docs/michi-development/testing-strategy.md +0 -87
  82. package/docs/plan.md +0 -275
  83. package/docs/user-guide/getting-started/github-token-setup.md +0 -510
  84. package/docs/user-guide/getting-started/new-repository-setup.md +0 -704
  85. package/docs/user-guide/getting-started/quick-start.md +0 -212
  86. package/docs/user-guide/getting-started/setup.md +0 -819
  87. package/docs/user-guide/guides/agent-skills-integration.md +0 -222
  88. package/docs/user-guide/guides/customization.md +0 -537
  89. package/docs/user-guide/guides/internationalization.md +0 -540
  90. package/docs/user-guide/guides/migration-guide.md +0 -138
  91. package/docs/user-guide/guides/multi-project.md +0 -368
  92. package/docs/user-guide/guides/multi-repo-guide.md +0 -1590
  93. package/docs/user-guide/guides/phase-automation.md +0 -419
  94. package/docs/user-guide/guides/workflow.md +0 -574
  95. package/docs/user-guide/hands-on/README.md +0 -142
  96. package/docs/user-guide/hands-on/claude-agent-setup.md +0 -597
  97. package/docs/user-guide/hands-on/claude-setup.md +0 -452
  98. package/docs/user-guide/hands-on/cursor-setup.md +0 -353
  99. package/docs/user-guide/hands-on/troubleshooting.md +0 -964
  100. package/docs/user-guide/hands-on/verification-checklist.md +0 -439
  101. package/docs/user-guide/hands-on/workflow-walkthrough.md +0 -1078
  102. package/docs/user-guide/reference/config.md +0 -589
  103. package/docs/user-guide/reference/multi-repo-api.md +0 -771
  104. package/docs/user-guide/reference/quick-reference.md +0 -297
  105. package/docs/user-guide/reference/security-test-payloads.md +0 -50
  106. package/docs/user-guide/reference/tasks-template.md +0 -550
  107. package/docs/user-guide/release/ci-setup-java.md +0 -114
  108. package/docs/user-guide/release/ci-setup-nodejs.md +0 -94
  109. package/docs/user-guide/release/ci-setup-php.md +0 -102
  110. package/docs/user-guide/release/ci-setup-troubleshooting.md +0 -94
  111. package/docs/user-guide/release/ci-setup.md +0 -188
  112. package/docs/user-guide/release/release-flow.md +0 -476
  113. package/docs/user-guide/templates/test-specs/README.md +0 -173
  114. package/docs/user-guide/templates/test-specs/e2e-test-spec-template.md +0 -553
  115. package/docs/user-guide/templates/test-specs/integration-test-spec-template.md +0 -435
  116. package/docs/user-guide/templates/test-specs/performance-test-spec-template.md +0 -454
  117. package/docs/user-guide/templates/test-specs/security-test-spec-template.md +0 -625
  118. package/docs/user-guide/templates/test-specs/unit-test-spec-template.md +0 -328
  119. package/docs/user-guide/testing/integration-tests.md +0 -312
  120. package/docs/user-guide/testing/tdd-cycle.md +0 -349
  121. package/docs/user-guide/testing/test-execution-flow.md +0 -396
  122. package/docs/user-guide/testing/test-failure-handling.md +0 -521
  123. package/docs/user-guide/testing/test-planning-flow.md +0 -185
  124. package/docs/user-guide/testing-strategy.md +0 -185
  125. package/docs/verification-guide.md +0 -518
  126. package/templates/cline/rules/atlassian-integration.md +0 -36
  127. package/templates/cline/rules/michi-core.md +0 -56
  128. package/templates/codex/AGENTS.override.md +0 -277
  129. package/templates/codex/prompts/confluence-sync.md +0 -177
  130. package/templates/codex/rules/README.md +0 -210
  131. package/templates/cursor/commands/kiro/kiro-spec-impl.md +0 -244
  132. package/templates/cursor/commands/kiro/kiro-spec-tasks.md +0 -354
  133. package/templates/cursor/commands/michi/confluence-sync.md +0 -76
  134. package/templates/cursor/commands/michi/project-switch.md +0 -69
  135. package/templates/cursor/rules/atlassian-mcp.mdc +0 -188
  136. package/templates/cursor/rules/github-ssot.mdc +0 -151
  137. package/templates/cursor/rules/multi-project.mdc +0 -81
  138. package/templates/gemini/commands/README.md +0 -41
  139. package/templates/gemini/rules/GEMINI.md +0 -80
@@ -1,628 +0,0 @@
1
- # Michi 設定統合設計書 - 実装詳細
2
-
3
- **バージョン**: 1.0
4
- **作成日**: 2025-01-11
5
- **ステータス**: Draft
6
- **親ドキュメント**: [config-unification.md](./config-unification.md)
7
-
8
- ---
9
-
10
- ## 6. 実装詳細
11
-
12
- ### 6.1 ConfigLoader クラス設計
13
-
14
- ConfigLoaderは、複数の設定ファイルを読み込み、優先順位に従ってマージし、型安全なアクセスを提供するクラスです。
15
-
16
- **ファイルパス:** `scripts/utils/config-loader-v2.ts`
17
-
18
- **責務:**
19
- 1. 複数の設定ファイルを読み込み
20
- 2. 優先順位に従ってマージ
21
- 3. バリデーション
22
- 4. 型安全なアクセス提供
23
- 5. キャッシュによるパフォーマンス向上
24
-
25
- **クラス定義:**
26
-
27
- ```typescript
28
- import { z } from 'zod';
29
- import { existsSync, readFileSync } from 'fs';
30
- import { join } from 'path';
31
- import * as dotenv from 'dotenv';
32
-
33
- /**
34
- * 設定の読み込み元
35
- */
36
- type ConfigSource =
37
- | 'global-env' // ~/.michi/global.env
38
- | 'global-config' // ~/.michi/config.json
39
- | 'project-meta' // .kiro/project.json
40
- | 'project-config' // .michi/config.json
41
- | 'project-env'; // .env
42
-
43
- /**
44
- * 読み込まれた設定(ソース情報付き)
45
- */
46
- interface ConfigWithSource<T> {
47
- value: T;
48
- source: ConfigSource;
49
- }
50
-
51
- /**
52
- * 統合設定
53
- */
54
- interface MergedConfig {
55
- // Atlassian認証
56
- atlassian: {
57
- url: string;
58
- email: string;
59
- apiToken: string;
60
- };
61
-
62
- // GitHub設定
63
- github: {
64
- org: string; // 組織名(グローバル設定から)
65
- token: string; // アクセストークン(グローバル設定から)
66
- repository: string; // フルURL(project.jsonから)
67
- repositoryShort: string; // "org/repo" 形式(自動抽出)
68
- repositoryOrg: string; // リポジトリの組織名(自動抽出)
69
- repositoryName: string; // リポジトリ名(自動抽出)
70
- };
71
-
72
- // Confluenceスペース
73
- confluence: {
74
- spaces: {
75
- prd: string;
76
- qa: string;
77
- release: string;
78
- };
79
- // 設定
80
- pageCreationGranularity: string;
81
- pageTitleFormat?: string;
82
- hierarchy?: unknown;
83
- };
84
-
85
- // JIRA設定
86
- jira: {
87
- issueTypes: {
88
- story: string;
89
- subtask: string;
90
- };
91
- projectKeys: string; // プロジェクト固有
92
- createEpic: boolean;
93
- storyCreationGranularity: string;
94
- selectedPhases?: string[];
95
- storyPoints: string;
96
- };
97
-
98
- // ワークフロー設定
99
- workflow: {
100
- enabledPhases: string[];
101
- approvalGates?: {
102
- requirements?: string[];
103
- design?: string[];
104
- release?: string[];
105
- };
106
- };
107
-
108
- // プロジェクトメタデータ
109
- project: {
110
- id: string;
111
- name: string;
112
- language: string;
113
- jiraProjectKey: string;
114
- confluenceLabels: string[];
115
- status: string;
116
- team: string[];
117
- stakeholders: string[];
118
- repository: string;
119
- description: string;
120
- };
121
- }
122
-
123
- /**
124
- * 読み込みオプション
125
- */
126
- interface LoadOptions {
127
- projectRoot?: string; // プロジェクトルート(デフォルト: process.cwd())
128
- skipValidation?: boolean; // バリデーションをスキップ
129
- useCache?: boolean; // キャッシュを使用(デフォルト: true)
130
- }
131
-
132
- /**
133
- * 設定ローダー
134
- */
135
- export class ConfigLoader {
136
- private cache: MergedConfig | null = null;
137
- private cacheTimestamp: number = 0;
138
- private cacheTTL: number = 60000; // 1分
139
-
140
- /**
141
- * 設定を読み込んでマージ
142
- */
143
- async load(options?: LoadOptions): Promise<MergedConfig> {
144
- // キャッシュチェック
145
- if (this.cache && options?.useCache !== false) {
146
- const now = Date.now();
147
- if (now - this.cacheTimestamp < this.cacheTTL) {
148
- return this.cache;
149
- }
150
- }
151
-
152
- const projectRoot = options?.projectRoot || process.cwd();
153
-
154
- // パフォーマンス計測開始
155
- const start = performance.now();
156
-
157
- // 並列読み込み
158
- const [globalEnv, globalConfig, projectMeta, projectConfig, projectEnv] =
159
- await Promise.all([
160
- this.loadGlobalEnv(),
161
- this.loadGlobalConfig(),
162
- this.loadProjectMeta(projectRoot),
163
- this.loadProjectConfig(projectRoot),
164
- this.loadProjectEnv(projectRoot),
165
- ]);
166
-
167
- // マージ
168
- const merged = this.merge([
169
- globalEnv,
170
- globalConfig,
171
- projectMeta,
172
- projectConfig,
173
- projectEnv,
174
- ]);
175
-
176
- // リポジトリURLのパース(project.jsonから取得)
177
- if (merged.project?.repository) {
178
- const parsed = this.parseGitHubRepository(merged.project.repository);
179
- merged.github = {
180
- ...merged.github,
181
- repository: parsed.url,
182
- repositoryShort: parsed.shortForm,
183
- repositoryOrg: parsed.org,
184
- repositoryName: parsed.repo,
185
- };
186
- }
187
-
188
- // バリデーション
189
- if (!options?.skipValidation) {
190
- this.validate(merged);
191
- }
192
-
193
- // キャッシュ更新
194
- this.cache = merged;
195
- this.cacheTimestamp = Date.now();
196
-
197
- // パフォーマンス計測終了
198
- const elapsed = performance.now() - start;
199
- if (elapsed > 100) {
200
- console.warn(`⚠️ 設定の読み込みに ${elapsed.toFixed(2)}ms かかりました(目標: <100ms)`);
201
- }
202
-
203
- return merged;
204
- }
205
-
206
- /**
207
- * 設定をリロード(キャッシュをクリア)
208
- */
209
- reload(): void {
210
- this.cache = null;
211
- this.cacheTimestamp = 0;
212
- }
213
-
214
- /**
215
- * 特定の設定値を取得
216
- */
217
- get<T>(path: string): T | undefined {
218
- if (!this.cache) {
219
- throw new Error('Configuration not loaded. Call load() first.');
220
- }
221
-
222
- // ドットパスで設定値を取得
223
- return this.getValueByPath(this.cache, path);
224
- }
225
-
226
- /**
227
- * 設定値の設定元を取得
228
- */
229
- getSource(path: string): ConfigSource | undefined {
230
- // TODO: 実装
231
- // 各設定値がどのファイルから来たかを追跡する
232
- return undefined;
233
- }
234
-
235
- /**
236
- * グローバル.envを読み込み
237
- */
238
- private async loadGlobalEnv(): Promise<Partial<MergedConfig>> {
239
- const globalEnvPath = this.getGlobalEnvPath();
240
- if (!existsSync(globalEnvPath)) {
241
- return {};
242
- }
243
-
244
- const parsed = dotenv.parse(readFileSync(globalEnvPath, 'utf-8'));
245
-
246
- return {
247
- atlassian: {
248
- url: parsed.ATLASSIAN_URL || '',
249
- email: parsed.ATLASSIAN_EMAIL || '',
250
- apiToken: parsed.ATLASSIAN_API_TOKEN || '',
251
- },
252
- github: {
253
- org: parsed.GITHUB_ORG || '',
254
- token: parsed.GITHUB_TOKEN || '',
255
- // repository関連はproject.jsonから取得
256
- repository: '',
257
- repositoryShort: '',
258
- repositoryOrg: '',
259
- repositoryName: '',
260
- },
261
- confluence: {
262
- spaces: {
263
- prd: parsed.CONFLUENCE_PRD_SPACE || 'PRD',
264
- qa: parsed.CONFLUENCE_QA_SPACE || 'QA',
265
- release: parsed.CONFLUENCE_RELEASE_SPACE || 'RELEASE',
266
- },
267
- },
268
- jira: {
269
- issueTypes: {
270
- story: parsed.JIRA_ISSUE_TYPE_STORY || '',
271
- subtask: parsed.JIRA_ISSUE_TYPE_SUBTASK || '',
272
- },
273
- projectKeys: '', // プロジェクト固有
274
- },
275
- };
276
- }
277
-
278
- /**
279
- * グローバル設定を読み込み
280
- */
281
- private async loadGlobalConfig(): Promise<Partial<MergedConfig>> {
282
- const globalConfigPath = this.getGlobalConfigPath();
283
- if (!existsSync(globalConfigPath)) {
284
- return {};
285
- }
286
-
287
- const content = readFileSync(globalConfigPath, 'utf-8');
288
- const config = JSON.parse(content);
289
-
290
- // TODO: 変換処理
291
- return config;
292
- }
293
-
294
- /**
295
- * プロジェクトメタデータを読み込み
296
- */
297
- private async loadProjectMeta(projectRoot: string): Promise<Partial<MergedConfig>> {
298
- const projectMetaPath = join(projectRoot, '.kiro/project.json');
299
- if (!existsSync(projectMetaPath)) {
300
- return {};
301
- }
302
-
303
- const content = readFileSync(projectMetaPath, 'utf-8');
304
- const meta = JSON.parse(content);
305
-
306
- return {
307
- project: meta,
308
- };
309
- }
310
-
311
- /**
312
- * プロジェクト設定を読み込み
313
- */
314
- private async loadProjectConfig(projectRoot: string): Promise<Partial<MergedConfig>> {
315
- const projectConfigPath = join(projectRoot, '.michi/config.json');
316
- if (!existsSync(projectConfigPath)) {
317
- return {};
318
- }
319
-
320
- const content = readFileSync(projectConfigPath, 'utf-8');
321
- const config = JSON.parse(content);
322
-
323
- // TODO: 変換処理
324
- return config;
325
- }
326
-
327
- /**
328
- * プロジェクト.envを読み込み
329
- */
330
- private async loadProjectEnv(projectRoot: string): Promise<Partial<MergedConfig>> {
331
- const projectEnvPath = join(projectRoot, '.env');
332
- if (!existsSync(projectEnvPath)) {
333
- return {};
334
- }
335
-
336
- const parsed = dotenv.parse(readFileSync(projectEnvPath, 'utf-8'));
337
-
338
- return {
339
- jira: {
340
- projectKeys: parsed.JIRA_PROJECT_KEYS || '',
341
- },
342
- // GITHUB_REPO は削除(project.jsonのrepositoryから取得)
343
- };
344
- }
345
-
346
- /**
347
- * マージ処理
348
- */
349
- private merge(configs: Partial<MergedConfig>[]): MergedConfig {
350
- // deep merge with priority
351
- return this.deepMerge({}, ...configs) as MergedConfig;
352
- }
353
-
354
- /**
355
- * ディープマージ
356
- */
357
- private deepMerge(target: any, ...sources: any[]): any {
358
- for (const source of sources) {
359
- if (!source) continue;
360
-
361
- for (const key in source) {
362
- const targetValue = target[key];
363
- const sourceValue = source[key];
364
-
365
- if (this.isObject(sourceValue) && this.isObject(targetValue)) {
366
- target[key] = this.deepMerge(targetValue, sourceValue);
367
- } else if (sourceValue !== undefined && sourceValue !== '') {
368
- target[key] = sourceValue;
369
- }
370
- }
371
- }
372
-
373
- return target;
374
- }
375
-
376
- /**
377
- * オブジェクトチェック
378
- */
379
- private isObject(value: any): boolean {
380
- return value !== null && typeof value === 'object' && !Array.isArray(value);
381
- }
382
-
383
- /**
384
- * バリデーション
385
- */
386
- private validate(config: MergedConfig): void {
387
- try {
388
- MergedConfigSchema.parse(config);
389
- } catch (error) {
390
- if (error instanceof z.ZodError) {
391
- throw new ConfigValidationError(
392
- 'Configuration validation failed',
393
- error.issues
394
- );
395
- }
396
- throw error;
397
- }
398
- }
399
-
400
- /**
401
- * パスで値を取得
402
- */
403
- private getValueByPath(obj: any, path: string): any {
404
- const keys = path.split('.');
405
- let current = obj;
406
-
407
- for (const key of keys) {
408
- if (current === undefined || current === null) {
409
- return undefined;
410
- }
411
- current = current[key];
412
- }
413
-
414
- return current;
415
- }
416
-
417
- /**
418
- * グローバル.envのパスを取得
419
- *
420
- * 注: 後方互換性のため、旧形式(global.env)も確認する
421
- */
422
- private getGlobalEnvPath(): string {
423
- const homeDir = process.env.HOME || process.env.USERPROFILE;
424
- if (!homeDir) {
425
- throw new Error('Could not determine home directory');
426
- }
427
-
428
- const newPath = join(homeDir, '.michi', '.env');
429
- const oldPath = join(homeDir, '.michi', 'global.env');
430
-
431
- // 旧形式が存在し、新形式が存在しない場合は警告
432
- if (existsSync(oldPath) && !existsSync(newPath)) {
433
- console.warn('⚠️ 古い設定ファイルが見つかりました: ~/.michi/global.env');
434
- console.warn(' ~/.michi/.env に移行してください');
435
- console.warn(' コマンド: michi migrate config');
436
- return oldPath; // 後方互換性のため旧パスを返す
437
- }
438
-
439
- return newPath;
440
- }
441
-
442
- /**
443
- * グローバル設定のパスを取得
444
- */
445
- private getGlobalConfigPath(): string {
446
- const homeDir = process.env.HOME || process.env.USERPROFILE;
447
- if (!homeDir) {
448
- throw new Error('Could not determine home directory');
449
- }
450
- return join(homeDir, '.michi', 'config.json');
451
- }
452
-
453
- /**
454
- * GitHubリポジトリURLをパース
455
- *
456
- * @param repoUrl - GitHub リポジトリURL(HTTPS または SSH)
457
- * @returns パースされたリポジトリ情報
458
- * @throws {ConfigValidationError} 無効なURLの場合
459
- */
460
- private parseGitHubRepository(repoUrl: string): {
461
- url: string;
462
- org: string;
463
- repo: string;
464
- shortForm: string;
465
- } {
466
- // HTTPSとSSH両方のURLをサポート
467
- const httpsMatch = repoUrl.match(/github\.com\/([^/]+)\/([^/]+?)(\.git)?$/);
468
- const sshMatch = repoUrl.match(/github\.com:([^/]+)\/([^/]+?)(\.git)?$/);
469
- const match = httpsMatch || sshMatch;
470
-
471
- if (!match) {
472
- throw new ConfigValidationError(
473
- 'REPOSITORY_URL_INVALID',
474
- `Invalid GitHub repository URL: ${repoUrl}`,
475
- []
476
- );
477
- }
478
-
479
- const org = match[1];
480
- const repo = match[2];
481
-
482
- return {
483
- url: repoUrl,
484
- org,
485
- repo,
486
- shortForm: `${org}/${repo}`,
487
- };
488
- }
489
- }
490
-
491
- /**
492
- * シングルトンインスタンス
493
- */
494
- export const configLoader = new ConfigLoader();
495
-
496
- /**
497
- * ヘルパー関数(後方互換性のため)
498
- */
499
- export async function loadConfig(options?: LoadOptions): Promise<MergedConfig> {
500
- return configLoader.load(options);
501
- }
502
- ```
503
-
504
- ### 6.2 Zodスキーマ定義
505
-
506
- ```typescript
507
- // scripts/utils/config-schema.ts
508
-
509
- import { z } from 'zod';
510
-
511
- /**
512
- * MergedConfig のZodスキーマ
513
- */
514
- export const MergedConfigSchema = z.object({
515
- atlassian: z.object({
516
- url: z.string().url(),
517
- email: z.string().email(),
518
- apiToken: z.string().min(1),
519
- }),
520
-
521
- github: z.object({
522
- org: z.string().min(1),
523
- token: z.string().min(1),
524
- repository: z.string().url(),
525
- repositoryShort: z.string().min(1),
526
- repositoryOrg: z.string().min(1),
527
- repositoryName: z.string().min(1),
528
- }),
529
-
530
- confluence: z.object({
531
- spaces: z.object({
532
- prd: z.string().min(1),
533
- qa: z.string().min(1),
534
- release: z.string().min(1),
535
- }),
536
- pageCreationGranularity: z.enum(['single', 'by-section', 'by-hierarchy', 'manual']),
537
- pageTitleFormat: z.string().optional(),
538
- hierarchy: z.any().optional(),
539
- }),
540
-
541
- jira: z.object({
542
- issueTypes: z.object({
543
- story: z.string().min(1),
544
- subtask: z.string().min(1),
545
- }),
546
- projectKeys: z.string().min(1),
547
- createEpic: z.boolean(),
548
- storyCreationGranularity: z.enum(['all', 'by-phase', 'selected-phases']),
549
- selectedPhases: z.array(z.string()).optional(),
550
- storyPoints: z.enum(['auto', 'manual', 'disabled']),
551
- }),
552
-
553
- workflow: z.object({
554
- enabledPhases: z.array(z.string()).min(1),
555
- approvalGates: z.object({
556
- requirements: z.array(z.string()).optional(),
557
- design: z.array(z.string()).optional(),
558
- release: z.array(z.string()).optional(),
559
- }).optional(),
560
- }),
561
-
562
- project: z.object({
563
- id: z.string().min(1),
564
- name: z.string().min(1),
565
- language: z.enum(['ja', 'en']),
566
- jiraProjectKey: z.string().regex(/^[A-Z]{2,10}$/),
567
- confluenceLabels: z.array(z.string()),
568
- status: z.string(),
569
- team: z.array(z.string()),
570
- stakeholders: z.array(z.string()),
571
- repository: z.string().url(),
572
- description: z.string(),
573
- }),
574
- });
575
-
576
- export type MergedConfig = z.infer<typeof MergedConfigSchema>;
577
- ```
578
-
579
- ### 6.3 エラークラス定義
580
-
581
- ```typescript
582
- // scripts/utils/config-errors.ts
583
-
584
- import { z } from 'zod';
585
-
586
- /**
587
- * 設定関連のエラー基底クラス
588
- */
589
- export class ConfigError extends Error {
590
- constructor(message: string, public code: string) {
591
- super(message);
592
- this.name = 'ConfigError';
593
- }
594
- }
595
-
596
- /**
597
- * バリデーションエラー
598
- */
599
- export class ConfigValidationError extends ConfigError {
600
- constructor(message: string, public details: z.ZodIssue[]) {
601
- super(message, 'CONFIG_VALIDATION_ERROR');
602
- this.name = 'ConfigValidationError';
603
- }
604
- }
605
-
606
- /**
607
- * 必須設定が見つからない
608
- */
609
- export class ConfigNotFoundError extends ConfigError {
610
- constructor(message: string, public missingKeys: string[]) {
611
- super(message, 'CONFIG_NOT_FOUND');
612
- this.name = 'ConfigNotFoundError';
613
- }
614
- }
615
-
616
- /**
617
- * マイグレーションエラー
618
- */
619
- export class MigrationError extends ConfigError {
620
- constructor(message: string, public phase: string) {
621
- super(message, 'MIGRATION_ERROR');
622
- this.name = 'MigrationError';
623
- }
624
- }
625
- ```
626
-
627
- ---
628
-