@sk8metal/michi-cli 0.0.2 → 0.0.4

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 (47) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +37 -0
  3. package/dist/scripts/config/config-schema.d.ts +109 -600
  4. package/dist/scripts/config/config-schema.d.ts.map +1 -1
  5. package/dist/scripts/config/config-schema.js.map +1 -1
  6. package/dist/scripts/config-interactive.js +3 -3
  7. package/dist/scripts/confluence-sync.js +3 -3
  8. package/dist/scripts/create-project.js.map +1 -1
  9. package/dist/scripts/jira-sync.js +5 -5
  10. package/dist/scripts/markdown-to-confluence.js +1 -1
  11. package/dist/scripts/markdown-to-confluence.js.map +1 -1
  12. package/dist/scripts/multi-project-estimate.js +1 -1
  13. package/dist/scripts/phase-runner.js.map +1 -1
  14. package/dist/scripts/pre-flight-check.js +1 -1
  15. package/dist/scripts/setup-existing-project.js.map +1 -1
  16. package/dist/scripts/utils/config-loader.js +1 -1
  17. package/dist/scripts/utils/config-loader.js.map +1 -1
  18. package/dist/scripts/utils/config-validator.d.ts.map +1 -1
  19. package/dist/scripts/utils/config-validator.js +46 -46
  20. package/dist/scripts/utils/config-validator.js.map +1 -1
  21. package/dist/scripts/utils/confluence-hierarchy.d.ts.map +1 -1
  22. package/dist/scripts/utils/confluence-hierarchy.js +9 -8
  23. package/dist/scripts/utils/confluence-hierarchy.js.map +1 -1
  24. package/dist/scripts/validate-phase.js.map +1 -1
  25. package/dist/scripts/workflow-orchestrator.js.map +1 -1
  26. package/dist/src/cli.js +0 -0
  27. package/dist/vitest.config.d.ts +3 -0
  28. package/dist/vitest.config.d.ts.map +1 -0
  29. package/dist/vitest.config.js +33 -0
  30. package/dist/vitest.config.js.map +1 -0
  31. package/docs/release.md +365 -0
  32. package/package.json +16 -12
  33. package/scripts/config/config-schema.ts +5 -5
  34. package/scripts/config-interactive.ts +3 -3
  35. package/scripts/confluence-sync.ts +3 -3
  36. package/scripts/create-project.ts +17 -17
  37. package/scripts/jira-sync.ts +5 -5
  38. package/scripts/markdown-to-confluence.ts +1 -1
  39. package/scripts/multi-project-estimate.ts +1 -1
  40. package/scripts/phase-runner.ts +8 -8
  41. package/scripts/pre-flight-check.ts +1 -1
  42. package/scripts/setup-existing-project.ts +9 -9
  43. package/scripts/utils/config-loader.ts +1 -1
  44. package/scripts/utils/config-validator.ts +47 -46
  45. package/scripts/utils/confluence-hierarchy.ts +52 -51
  46. package/scripts/validate-phase.ts +11 -11
  47. package/scripts/workflow-orchestrator.ts +27 -27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sk8metal/michi-cli",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Managed Intelligent Comprehensive Hub for Integration - AI-driven development workflow automation",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "repository": {
11
11
  "type": "git",
12
- "url": "https://github.com/sk8metalme/michi.git"
12
+ "url": "git+https://github.com/sk8metalme/michi.git"
13
13
  },
14
14
  "homepage": "https://github.com/sk8metalme/michi#readme",
15
15
  "bugs": {
@@ -29,7 +29,7 @@
29
29
  "github"
30
30
  ],
31
31
  "bin": {
32
- "michi": "./dist/src/cli.js"
32
+ "michi": "dist/src/cli.js"
33
33
  },
34
34
  "files": [
35
35
  "dist",
@@ -64,33 +64,37 @@
64
64
  "test": "vitest",
65
65
  "test:run": "vitest --run",
66
66
  "test:ui": "vitest --ui",
67
+ "test:coverage": "vitest --run --coverage",
67
68
  "lint": "eslint .",
68
69
  "lint:fix": "eslint . --fix",
69
70
  "type-check": "tsc --noEmit"
70
71
  },
71
72
  "dependencies": {
72
- "@octokit/rest": "^20.0.2",
73
+ "@octokit/rest": "^22.0.1",
73
74
  "axios": "^1.13.1",
74
75
  "commander": "^14.0.2",
75
76
  "dotenv": "^16.3.1",
76
77
  "exceljs": "^4.4.0",
77
- "googleapis": "^126.0.1",
78
+ "googleapis": "^166.0.0",
78
79
  "jira-client": "^8.2.2",
79
80
  "markdown-it": "^14.0.0",
80
81
  "turndown": "^7.1.2",
81
- "zod": "^3.22.4"
82
+ "zod": "^4.1.12"
82
83
  },
83
84
  "devDependencies": {
84
- "@types/markdown-it": "^13.0.7",
85
- "@types/node": "^20.10.6",
85
+ "@eslint/js": "^9.39.1",
86
+ "@types/markdown-it": "^14.1.2",
87
+ "@types/node": "^24.10.1",
86
88
  "@types/turndown": "^5.0.4",
87
- "@typescript-eslint/eslint-plugin": "^6.17.0",
88
- "@typescript-eslint/parser": "^6.17.0",
89
- "eslint": "^8.56.0",
89
+ "@typescript-eslint/eslint-plugin": "^8.46.4",
90
+ "@typescript-eslint/parser": "^8.46.4",
91
+ "eslint": "^9.39.1",
90
92
  "prettier": "^3.1.1",
91
93
  "tsx": "^4.7.0",
92
94
  "typescript": "^5.3.3",
93
- "vitest": "^1.1.1"
95
+ "typescript-eslint": "^8.46.4",
96
+ "vitest": "^4.0.8",
97
+ "@vitest/coverage-v8": "^4.0.8"
94
98
  },
95
99
  "engines": {
96
100
  "node": ">=20.0.0",
@@ -86,11 +86,11 @@ export const JiraConfigSchema = z.object({
86
86
  createEpic: z.boolean().default(true),
87
87
  storyPoints: JiraStoryPointsSchema.default('auto'),
88
88
  autoLabels: z.array(z.string()).default(['{projectLabel}', '{featureName}', '{phaseLabel}']),
89
- issueTypes: z.object({
90
- epic: z.string().default('Epic'),
91
- story: z.string().nullish().default(null), // null | undefined | string
92
- subtask: z.string().nullish().default(null) // null | undefined | string
93
- }).optional(),
89
+ issueTypes: z.object({
90
+ epic: z.string().default('Epic'),
91
+ story: z.string().nullish().default(null), // null | undefined | string
92
+ subtask: z.string().nullish().default(null) // null | undefined | string
93
+ }).optional(),
94
94
  selectedPhases: z.array(z.string()).optional()
95
95
  });
96
96
 
@@ -102,7 +102,7 @@ async function multiSelect(
102
102
  console.log(` ${checked} ${index + 1}. ${choice.label}`);
103
103
  });
104
104
 
105
- const answer = await question(rl, `\n選択してください(カンマ区切り、例: 1,2,3): `);
105
+ const answer = await question(rl, '\n選択してください(カンマ区切り、例: 1,2,3): ');
106
106
 
107
107
  if (!answer && defaults.length > 0) {
108
108
  return defaults;
@@ -204,13 +204,13 @@ async function getConfluenceConfig(rl: readline.Interface, projectMeta: any): Pr
204
204
 
205
205
  const parentTitle = await question(
206
206
  rl,
207
- `親ページのタイトル形式を入力してください(例: [{projectName}] {featureName}): `
207
+ '親ページのタイトル形式を入力してください(例: [{projectName}] {featureName}): '
208
208
  );
209
209
 
210
210
  if (parentTitle) {
211
211
  config.hierarchy.parentPageTitle = parentTitle;
212
212
  } else {
213
- config.hierarchy.parentPageTitle = `[{projectName}] {featureName}`;
213
+ config.hierarchy.parentPageTitle = '[{projectName}] {featureName}';
214
214
  }
215
215
 
216
216
  if (granularity === 'manual') {
@@ -107,7 +107,7 @@ class ConfluenceClient {
107
107
  }
108
108
 
109
109
  // CQLクエリで見つからない場合、従来の方法で検索(親ページIDでフィルタリング)
110
- console.log(` Falling back to standard search (may find pages in different parent)`);
110
+ console.log(' Falling back to standard search (may find pages in different parent)');
111
111
  return null;
112
112
  }
113
113
 
@@ -388,7 +388,7 @@ async function syncToConfluence(
388
388
  } else if (process.env.CONFLUENCE_PRD_SPACE) {
389
389
  console.log(`📝 Config source: environment variable (CONFLUENCE_PRD_SPACE = ${process.env.CONFLUENCE_PRD_SPACE})`);
390
390
  } else {
391
- console.log(`📝 Config source: default config`);
391
+ console.log('📝 Config source: default config');
392
392
  }
393
393
 
394
394
  // Markdownファイル読み込み
@@ -446,7 +446,7 @@ async function syncToConfluence(
446
446
  console.log(`✅ Sync completed: ${result.pages.length} page(s) created/updated`);
447
447
 
448
448
  if (result.pages.length > 1) {
449
- console.log(`📄 Created pages:`);
449
+ console.log('📄 Created pages:');
450
450
  result.pages.forEach((page, index) => {
451
451
  console.log(` ${index + 1}. ${page.title} - ${page.url}`);
452
452
  });
@@ -38,22 +38,22 @@ function parseArgs(): ProjectConfig {
38
38
  const value = args[i + 1];
39
39
 
40
40
  switch (key) {
41
- case 'name':
42
- config.name = value;
43
- break;
44
- case 'project-name':
45
- config.projectName = value;
46
- break;
47
- case 'jira-key':
48
- config.jiraKey = value;
49
- break;
50
- case 'org':
51
- config.org = value;
52
- break;
53
- case 'labels':
54
- // カンマ区切りでラベル配列に変換
55
- config.labels = value.split(',').map(l => l.trim());
56
- break;
41
+ case 'name':
42
+ config.name = value;
43
+ break;
44
+ case 'project-name':
45
+ config.projectName = value;
46
+ break;
47
+ case 'jira-key':
48
+ config.jiraKey = value;
49
+ break;
50
+ case 'org':
51
+ config.org = value;
52
+ break;
53
+ case 'labels':
54
+ // カンマ区切りでラベル配列に変換
55
+ config.labels = value.split(',').map(l => l.trim());
56
+ break;
57
57
  }
58
58
  }
59
59
 
@@ -133,7 +133,7 @@ async function createProject(config: ProjectConfig): Promise<void> {
133
133
 
134
134
  // ハイフンが存在する場合のみサービスラベルを生成
135
135
  if (repoName.includes('-')) {
136
- const parts = repoName.split('-');
136
+ const parts = repoName.split('-');
137
137
  const servicePart = parts[parts.length - 1];
138
138
  const serviceLabel = servicePart.toLowerCase().replace(/[^a-z0-9-]/g, '');
139
139
 
@@ -600,13 +600,13 @@ async function syncTasksToJIRA(featureName: string): Promise<void> {
600
600
 
601
601
  // JIRA APIエラーの詳細を表示
602
602
  if (error.response?.data) {
603
- console.error(` 📋 JIRA API Error Details:`, JSON.stringify(error.response.data, null, 2));
603
+ console.error(' 📋 JIRA API Error Details:', JSON.stringify(error.response.data, null, 2));
604
604
 
605
605
  // Story Pointsフィールドのエラーの場合、警告を表示
606
606
  if (error.response.data.errors && Object.keys(error.response.data.errors).some(key => key.includes('customfield'))) {
607
- console.error(` ⚠️ Story Pointsフィールドの設定に失敗しました。`);
608
- console.error(` 💡 環境変数 JIRA_STORY_POINTS_FIELD を正しいカスタムフィールドIDに設定してください。`);
609
- console.error(` 💡 JIRA管理画面でStory PointsのカスタムフィールドIDを確認してください。`);
607
+ console.error(' ⚠️ Story Pointsフィールドの設定に失敗しました。');
608
+ console.error(' 💡 環境変数 JIRA_STORY_POINTS_FIELD を正しいカスタムフィールドIDに設定してください。');
609
+ console.error(' 💡 JIRA管理画面でStory PointsのカスタムフィールドIDを確認してください。');
610
610
  }
611
611
  }
612
612
 
@@ -618,7 +618,7 @@ async function syncTasksToJIRA(featureName: string): Promise<void> {
618
618
  const newStoryCount = createdStories.filter(key => !existingStoryKeys.has(key)).length;
619
619
  const reusedStoryCount = createdStories.filter(key => existingStoryKeys.has(key)).length;
620
620
 
621
- console.log(`\n✅ JIRA sync completed`);
621
+ console.log('\n✅ JIRA sync completed');
622
622
  console.log(` Epic: ${epic.key}`);
623
623
  console.log(` Stories: ${createdStories.length} processed (${newStoryCount} new, ${reusedStoryCount} reused)`);
624
624
  }
@@ -93,7 +93,7 @@ function decodeHtmlEntities(text: string): string {
93
93
  '&gt;': '>',
94
94
  '&amp;': '&',
95
95
  '&quot;': '"',
96
- '&#39;': "'",
96
+ '&#39;': '\'',
97
97
  '&nbsp;': ' '
98
98
  };
99
99
 
@@ -153,7 +153,7 @@ function parseEstimateFromContent(content: string, featureName: string): Estimat
153
153
 
154
154
  return estimate;
155
155
  } catch (error) {
156
- console.warn(` ⚠️ Failed to parse estimate:`, error instanceof Error ? error.message : error);
156
+ console.warn(' ⚠️ Failed to parse estimate:', error instanceof Error ? error.message : error);
157
157
  return null;
158
158
  } finally {
159
159
  // 一時ファイルをクリーンアップ
@@ -256,14 +256,14 @@ export async function runPhase(feature: string, phase: Phase): Promise<PhaseRunR
256
256
  validateFeatureNameOrThrow(feature);
257
257
 
258
258
  switch (phase) {
259
- case 'requirements':
260
- return await runRequirementsPhase(feature);
261
- case 'design':
262
- return await runDesignPhase(feature);
263
- case 'tasks':
264
- return await runTasksPhase(feature);
265
- default:
266
- throw new Error(`Unknown phase: ${phase}`);
259
+ case 'requirements':
260
+ return await runRequirementsPhase(feature);
261
+ case 'design':
262
+ return await runDesignPhase(feature);
263
+ case 'tasks':
264
+ return await runTasksPhase(feature);
265
+ default:
266
+ throw new Error(`Unknown phase: ${phase}`);
267
267
  }
268
268
  }
269
269
 
@@ -178,7 +178,7 @@ async function checkJiraProject(projectKey: string): Promise<{ errors: string[],
178
178
  errors.push(` 現在の設定: "${projectKey}" → 実際に存在するキーに変更`);
179
179
  } else if (error.response?.status === 401) {
180
180
  errors.push('❌ JIRA認証エラー(.envの認証情報を確認)');
181
- errors.push(` → API Token管理: https://id.atlassian.com/manage-profile/security/api-tokens`);
181
+ errors.push(' → API Token管理: https://id.atlassian.com/manage-profile/security/api-tokens');
182
182
  } else {
183
183
  warnings.push(`⚠️ JIRAプロジェクトチェック失敗: ${error.message}`);
184
184
  }
@@ -30,15 +30,15 @@ function parseArgs(): SetupConfig {
30
30
  const value = args[i + 1];
31
31
 
32
32
  switch (key) {
33
- case 'michi-path':
34
- config.michiPath = value;
35
- break;
36
- case 'project-name':
37
- config.projectName = value;
38
- break;
39
- case 'jira-key':
40
- config.jiraKey = value;
41
- break;
33
+ case 'michi-path':
34
+ config.michiPath = value;
35
+ break;
36
+ case 'project-name':
37
+ config.projectName = value;
38
+ break;
39
+ case 'jira-key':
40
+ config.jiraKey = value;
41
+ break;
42
42
  }
43
43
  }
44
44
 
@@ -192,7 +192,7 @@ export function loadConfig(projectRoot: string = process.cwd()): AppConfig {
192
192
  const projectConfig = loadProjectConfig(projectRoot);
193
193
 
194
194
  // マージ(プロジェクト設定がデフォルトを上書き)
195
- let mergedConfig: AppConfig = projectConfig
195
+ const mergedConfig: AppConfig = projectConfig
196
196
  ? deepMerge(defaultConfig, projectConfig)
197
197
  : defaultConfig;
198
198
 
@@ -5,6 +5,7 @@
5
5
  import { existsSync, readFileSync } from 'fs';
6
6
  import { resolve } from 'path';
7
7
  import { AppConfigSchema } from '../config/config-schema.js';
8
+ import type { ZodIssue } from 'zod';
8
9
  import type { AppConfig } from '../config/config-schema.js';
9
10
  import { getConfig } from './config-loader.js';
10
11
 
@@ -47,8 +48,8 @@ export function validateProjectConfig(projectRoot: string = process.cwd()): Vali
47
48
  const result = AppConfigSchema.safeParse(parsed);
48
49
 
49
50
  if (!result.success) {
50
- result.error.errors.forEach(error => {
51
- const path = error.path.join('.');
51
+ result.error.issues.forEach((error: ZodIssue) => {
52
+ const path = error.path.map(String).join('.');
52
53
  errors.push(`${path}: ${error.message}`);
53
54
  });
54
55
 
@@ -196,15 +197,15 @@ export function validateForConfluenceSync(
196
197
  if (!process.env.CONFLUENCE_PRD_SPACE) {
197
198
  warnings.push(
198
199
  `confluence.spaces.${docType}が設定されていません。` +
199
- `環境変数CONFLUENCE_PRD_SPACEも設定されていないため、デフォルト値(PRD)を使用します。` +
200
- `\n 推奨: .kiro/config.jsonに以下を追加してください:\n` +
201
- ` {\n` +
202
- ` "confluence": {\n` +
203
- ` "spaces": {\n` +
200
+ '環境変数CONFLUENCE_PRD_SPACEも設定されていないため、デフォルト値(PRD)を使用します。' +
201
+ '\n 推奨: .kiro/config.jsonに以下を追加してください:\n' +
202
+ ' {\n' +
203
+ ' "confluence": {\n' +
204
+ ' "spaces": {\n' +
204
205
  ` "${docType}": "YOUR_SPACE_KEY"\n` +
205
- ` }\n` +
206
- ` }\n` +
207
- ` }`
206
+ ' }\n' +
207
+ ' }\n' +
208
+ ' }'
208
209
  );
209
210
  } else {
210
211
  info.push(`confluence.spaces.${docType}が設定されていませんが、環境変数CONFLUENCE_PRD_SPACE(${process.env.CONFLUENCE_PRD_SPACE})を使用します。`);
@@ -215,29 +216,29 @@ export function validateForConfluenceSync(
215
216
  if (confluence.pageCreationGranularity === 'by-hierarchy' || confluence.pageCreationGranularity === 'manual') {
216
217
  if (!confluence.hierarchy) {
217
218
  errors.push(
218
- `confluence.hierarchyが設定されていません。` +
219
+ 'confluence.hierarchyが設定されていません。' +
219
220
  `pageCreationGranularityが"${confluence.pageCreationGranularity}"の場合、hierarchy設定が必須です。` +
220
- `\n 解決方法: .kiro/config.jsonに以下を追加してください:\n` +
221
- ` {\n` +
222
- ` "confluence": {\n` +
223
- ` "hierarchy": {\n` +
224
- ` "mode": "simple",\n` +
225
- ` "parentPageTitle": "[{projectName}] {featureName}"\n` +
226
- ` }\n` +
227
- ` }\n` +
228
- ` }`
221
+ '\n 解決方法: .kiro/config.jsonに以下を追加してください:\n' +
222
+ ' {\n' +
223
+ ' "confluence": {\n' +
224
+ ' "hierarchy": {\n' +
225
+ ' "mode": "simple",\n' +
226
+ ' "parentPageTitle": "[{projectName}] {featureName}"\n' +
227
+ ' }\n' +
228
+ ' }\n' +
229
+ ' }'
229
230
  );
230
231
  } else if (confluence.pageCreationGranularity === 'by-hierarchy' && confluence.hierarchy && !confluence.hierarchy.parentPageTitle) {
231
232
  warnings.push(
232
- `confluence.hierarchy.parentPageTitleが設定されていません。` +
233
- `by-hierarchyモードでは推奨されます。`
233
+ 'confluence.hierarchy.parentPageTitleが設定されていません。' +
234
+ 'by-hierarchyモードでは推奨されます。'
234
235
  );
235
236
  }
236
237
 
237
238
  if (confluence.pageCreationGranularity === 'manual' && confluence.hierarchy && !confluence.hierarchy.structure) {
238
239
  errors.push(
239
- `confluence.hierarchy.structureが設定されていません。` +
240
- `pageCreationGranularityが"manual"の場合、structure設定が必須です。`
240
+ 'confluence.hierarchy.structureが設定されていません。' +
241
+ 'pageCreationGranularityが"manual"の場合、structure設定が必須です。'
241
242
  );
242
243
  }
243
244
  }
@@ -274,20 +275,20 @@ export function validateForJiraSync(projectRoot: string = process.cwd()): Valida
274
275
  if (!jira.issueTypes) {
275
276
  if (!process.env.JIRA_ISSUE_TYPE_STORY) {
276
277
  errors.push(
277
- `jira.issueTypes.storyが設定されていません。` +
278
- `環境変数JIRA_ISSUE_TYPE_STORYも設定されていないため、JIRA同期を実行できません。` +
279
- `\n 解決方法1: 環境変数を設定:\n` +
280
- ` export JIRA_ISSUE_TYPE_STORY=10036 # JIRAインスタンス固有のID\n` +
281
- `\n 解決方法2: .kiro/config.jsonに以下を追加:\n` +
282
- ` {\n` +
283
- ` "jira": {\n` +
284
- ` "issueTypes": {\n` +
285
- ` "story": "10036",\n` +
286
- ` "subtask": "10037"\n` +
287
- ` }\n` +
288
- ` }\n` +
289
- ` }` +
290
- `\n 確認方法: JIRA管理画面(Settings > Issues > Issue types)またはREST API: GET /rest/api/3/issuetype`
278
+ 'jira.issueTypes.storyが設定されていません。' +
279
+ '環境変数JIRA_ISSUE_TYPE_STORYも設定されていないため、JIRA同期を実行できません。' +
280
+ '\n 解決方法1: 環境変数を設定:\n' +
281
+ ' export JIRA_ISSUE_TYPE_STORY=10036 # JIRAインスタンス固有のID\n' +
282
+ '\n 解決方法2: .kiro/config.jsonに以下を追加:\n' +
283
+ ' {\n' +
284
+ ' "jira": {\n' +
285
+ ' "issueTypes": {\n' +
286
+ ' "story": "10036",\n' +
287
+ ' "subtask": "10037"\n' +
288
+ ' }\n' +
289
+ ' }\n' +
290
+ ' }' +
291
+ '\n 確認方法: JIRA管理画面(Settings > Issues > Issue types)またはREST API: GET /rest/api/3/issuetype'
291
292
  );
292
293
  } else {
293
294
  info.push(`jira.issueTypes.storyが設定されていませんが、環境変数JIRA_ISSUE_TYPE_STORY(${process.env.JIRA_ISSUE_TYPE_STORY})を使用します。`);
@@ -296,10 +297,10 @@ export function validateForJiraSync(projectRoot: string = process.cwd()): Valida
296
297
  if (!jira.issueTypes.story) {
297
298
  if (!process.env.JIRA_ISSUE_TYPE_STORY) {
298
299
  errors.push(
299
- `jira.issueTypes.storyが設定されていません。` +
300
- `環境変数JIRA_ISSUE_TYPE_STORYも設定されていないため、JIRA同期を実行できません。` +
301
- `\n 解決方法: .kiro/config.jsonのjira.issueTypes.storyに値を設定するか、` +
302
- `環境変数JIRA_ISSUE_TYPE_STORYを設定してください。`
300
+ 'jira.issueTypes.storyが設定されていません。' +
301
+ '環境変数JIRA_ISSUE_TYPE_STORYも設定されていないため、JIRA同期を実行できません。' +
302
+ '\n 解決方法: .kiro/config.jsonのjira.issueTypes.storyに値を設定するか、' +
303
+ '環境変数JIRA_ISSUE_TYPE_STORYを設定してください。'
303
304
  );
304
305
  } else {
305
306
  info.push(`jira.issueTypes.storyが設定されていませんが、環境変数JIRA_ISSUE_TYPE_STORY(${process.env.JIRA_ISSUE_TYPE_STORY})を使用します。`);
@@ -309,8 +310,8 @@ export function validateForJiraSync(projectRoot: string = process.cwd()): Valida
309
310
  if (!jira.issueTypes.subtask) {
310
311
  if (!process.env.JIRA_ISSUE_TYPE_SUBTASK) {
311
312
  warnings.push(
312
- `jira.issueTypes.subtaskが設定されていません。` +
313
- `環境変数JIRA_ISSUE_TYPE_SUBTASKも設定されていないため、サブタスクは作成されません。`
313
+ 'jira.issueTypes.subtaskが設定されていません。' +
314
+ '環境変数JIRA_ISSUE_TYPE_SUBTASKも設定されていないため、サブタスクは作成されません。'
314
315
  );
315
316
  } else {
316
317
  info.push(`jira.issueTypes.subtaskが設定されていませんが、環境変数JIRA_ISSUE_TYPE_SUBTASK(${process.env.JIRA_ISSUE_TYPE_SUBTASK})を使用します。`);
@@ -321,8 +322,8 @@ export function validateForJiraSync(projectRoot: string = process.cwd()): Valida
321
322
  // selectedPhases設定のチェック
322
323
  if (jira.storyCreationGranularity === 'selected-phases' && !jira.selectedPhases) {
323
324
  errors.push(
324
- `jira.selectedPhasesが設定されていません。` +
325
- `storyCreationGranularityが"selected-phases"の場合、selectedPhases設定が必須です。`
325
+ 'jira.selectedPhasesが設定されていません。' +
326
+ 'storyCreationGranularityが"selected-phases"の場合、selectedPhases設定が必須です。'
326
327
  );
327
328
  }
328
329
 
@@ -401,7 +401,7 @@ export async function createByHierarchySimplePages(
401
401
 
402
402
  // タイトルに機能名が含まれていない場合、自動的に追加(重複を避けるため)
403
403
  if (!childPageTitle.includes(featureName)) {
404
- console.warn(`⚠️ Warning: pageTitleFormat does not include {featureName}. Adding feature name to ensure uniqueness.`);
404
+ console.warn('⚠️ Warning: pageTitleFormat does not include {featureName}. Adding feature name to ensure uniqueness.');
405
405
  childPageTitle = `[${featureName}] ${childPageTitle}`;
406
406
  }
407
407
 
@@ -423,7 +423,7 @@ export async function createByHierarchySimplePages(
423
423
 
424
424
  // CQLクエリで見つからない場合、親ページIDなしで検索(既存ページが別の親の下にある可能性)
425
425
  if (!existingChild) {
426
- console.log(` CQL search found nothing, trying search without parent filter`);
426
+ console.log(' CQL search found nothing, trying search without parent filter');
427
427
  existingChild = await client.searchPage(spaceKey, childPageTitle);
428
428
  if (existingChild) {
429
429
  console.log(` ⚠️ Found page with same title: ${existingChild.id}, verifying parent page ID`);
@@ -436,14 +436,14 @@ export async function createByHierarchySimplePages(
436
436
  console.log(` ✅ Parent page ID matches (${parentPageId}), proceeding with update`);
437
437
  } else {
438
438
  // 親ページIDが一致しない場合、エラーをスロー
439
- console.error(` ❌ Parent page ID mismatch!`);
439
+ console.error(' ❌ Parent page ID mismatch!');
440
440
  console.error(` Expected parent: ${parentPageId}`);
441
441
  console.error(` Actual parent: ${actualParentId || 'root (no parent)'}`);
442
442
  console.error(` Page ID: ${existingChild.id}`);
443
443
  throw new Error(
444
444
  `Page conflict: Found page "${childPageTitle}" (ID: ${existingChild.id}) ` +
445
445
  `under different parent (expected: ${parentPageId}, actual: ${actualParentId || 'root'}). ` +
446
- `Cannot update foreign page. Please rename or delete the conflicting page.`
446
+ 'Cannot update foreign page. Please rename or delete the conflicting page.'
447
447
  );
448
448
  }
449
449
  }
@@ -523,7 +523,7 @@ export async function createByHierarchyNestedPages(
523
523
 
524
524
  // タイトルに機能名が含まれていない場合、自動的に追加(重複を避けるため)
525
525
  if (!docTypeParentTitle.includes(featureName)) {
526
- console.warn(`⚠️ Warning: pageTitleFormat does not include {featureName}. Adding feature name to ensure uniqueness.`);
526
+ console.warn('⚠️ Warning: pageTitleFormat does not include {featureName}. Adding feature name to ensure uniqueness.');
527
527
  docTypeParentTitle = `[${featureName}] ${docTypeParentTitle}`;
528
528
  }
529
529
 
@@ -569,7 +569,7 @@ export async function createByHierarchyNestedPages(
569
569
 
570
570
  // タイトルに機能名が含まれていない場合、自動的に追加(重複を避けるため)
571
571
  if (!sectionPageTitle.includes(featureName)) {
572
- console.warn(`⚠️ Warning: pageTitleFormat does not include {featureName}. Adding feature name to ensure uniqueness.`);
572
+ console.warn('⚠️ Warning: pageTitleFormat does not include {featureName}. Adding feature name to ensure uniqueness.');
573
573
  sectionPageTitle = `[${featureName}] ${sectionPageTitle}`;
574
574
  }
575
575
  const confluenceContent = convertMarkdownToConfluence(section.content);
@@ -697,7 +697,7 @@ export async function createManualPages(
697
697
 
698
698
  // タイトルに機能名が含まれていない場合、自動的に追加(重複を避けるため)
699
699
  if (!pageTitle.includes(featureName)) {
700
- console.warn(`⚠️ Warning: Manual page title does not include {featureName}. Adding feature name to ensure uniqueness.`);
700
+ console.warn('⚠️ Warning: Manual page title does not include {featureName}. Adding feature name to ensure uniqueness.');
701
701
  pageTitle = `[${featureName}] ${pageTitle}`;
702
702
  }
703
703
 
@@ -785,20 +785,34 @@ export async function createPagesByGranularity(
785
785
  const granularity = config.pageCreationGranularity || 'single';
786
786
 
787
787
  switch (granularity) {
788
- case 'single':
789
- return await createSinglePage(
790
- client,
791
- spaceKey,
792
- markdown,
793
- config,
794
- projectMeta,
795
- featureName,
796
- docType,
797
- githubUrl
798
- );
788
+ case 'single':
789
+ return await createSinglePage(
790
+ client,
791
+ spaceKey,
792
+ markdown,
793
+ config,
794
+ projectMeta,
795
+ featureName,
796
+ docType,
797
+ githubUrl
798
+ );
799
799
 
800
- case 'by-section':
801
- return await createBySectionPages(
800
+ case 'by-section':
801
+ return await createBySectionPages(
802
+ client,
803
+ spaceKey,
804
+ markdown,
805
+ config,
806
+ projectMeta,
807
+ featureName,
808
+ docType,
809
+ githubUrl
810
+ );
811
+
812
+ case 'by-hierarchy': {
813
+ const hierarchyMode = config.hierarchy?.mode || 'simple';
814
+ if (hierarchyMode === 'nested') {
815
+ return await createByHierarchyNestedPages(
802
816
  client,
803
817
  spaceKey,
804
818
  markdown,
@@ -808,35 +822,8 @@ export async function createPagesByGranularity(
808
822
  docType,
809
823
  githubUrl
810
824
  );
811
-
812
- case 'by-hierarchy':
813
- const hierarchyMode = config.hierarchy?.mode || 'simple';
814
- if (hierarchyMode === 'nested') {
815
- return await createByHierarchyNestedPages(
816
- client,
817
- spaceKey,
818
- markdown,
819
- config,
820
- projectMeta,
821
- featureName,
822
- docType,
823
- githubUrl
824
- );
825
- } else {
826
- return await createByHierarchySimplePages(
827
- client,
828
- spaceKey,
829
- markdown,
830
- config,
831
- projectMeta,
832
- featureName,
833
- docType,
834
- githubUrl
835
- );
836
- }
837
-
838
- case 'manual':
839
- return await createManualPages(
825
+ } else {
826
+ return await createByHierarchySimplePages(
840
827
  client,
841
828
  spaceKey,
842
829
  markdown,
@@ -846,9 +833,23 @@ export async function createPagesByGranularity(
846
833
  docType,
847
834
  githubUrl
848
835
  );
836
+ }
837
+ }
838
+
839
+ case 'manual':
840
+ return await createManualPages(
841
+ client,
842
+ spaceKey,
843
+ markdown,
844
+ config,
845
+ projectMeta,
846
+ featureName,
847
+ docType,
848
+ githubUrl
849
+ );
849
850
 
850
- default:
851
- throw new Error(`Unknown page creation granularity: ${granularity}`);
851
+ default:
852
+ throw new Error(`Unknown page creation granularity: ${granularity}`);
852
853
  }
853
854
  }
854
855