@sk8metal/michi-cli 0.3.0 → 0.5.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 +83 -0
- package/dist/scripts/__tests__/spec-impl-workflow.test.js +4 -2
- package/dist/scripts/__tests__/spec-impl-workflow.test.js.map +1 -1
- package/dist/scripts/config/config-schema.d.ts +52 -0
- package/dist/scripts/config/config-schema.d.ts.map +1 -1
- package/dist/scripts/config/config-schema.js +25 -0
- package/dist/scripts/config/config-schema.js.map +1 -1
- package/dist/scripts/config-global.d.ts +10 -0
- package/dist/scripts/config-global.d.ts.map +1 -0
- package/dist/scripts/config-global.js +111 -0
- package/dist/scripts/config-global.js.map +1 -0
- package/dist/scripts/confluence-sync.d.ts +22 -4
- package/dist/scripts/confluence-sync.d.ts.map +1 -1
- package/dist/scripts/confluence-sync.js +22 -12
- package/dist/scripts/confluence-sync.js.map +1 -1
- package/dist/scripts/jira-sync.d.ts.map +1 -1
- package/dist/scripts/jira-sync.js +201 -167
- package/dist/scripts/jira-sync.js.map +1 -1
- package/dist/scripts/list-projects.js.map +1 -1
- package/dist/scripts/multi-project-estimate.js.map +1 -1
- package/dist/scripts/phase-runner.d.ts +1 -1
- package/dist/scripts/phase-runner.d.ts.map +1 -1
- package/dist/scripts/phase-runner.js +295 -522
- package/dist/scripts/phase-runner.js.map +1 -1
- package/dist/scripts/pr-automation.d.ts.map +1 -1
- package/dist/scripts/pr-automation.js +11 -3
- package/dist/scripts/pr-automation.js.map +1 -1
- package/dist/scripts/pre-flight-check.d.ts.map +1 -1
- package/dist/scripts/pre-flight-check.js +10 -6
- package/dist/scripts/pre-flight-check.js.map +1 -1
- package/dist/scripts/resource-dashboard.js.map +1 -1
- package/dist/scripts/spec-impl-workflow.d.ts.map +1 -1
- package/dist/scripts/spec-impl-workflow.js +23 -7
- package/dist/scripts/spec-impl-workflow.js.map +1 -1
- package/dist/scripts/template/renderer.d.ts +1 -1
- package/dist/scripts/template/renderer.d.ts.map +1 -1
- package/dist/scripts/test-interactive.d.ts.map +1 -1
- package/dist/scripts/test-interactive.js +0 -15
- package/dist/scripts/test-interactive.js.map +1 -1
- package/dist/scripts/test-new-features.js +6 -3
- package/dist/scripts/test-new-features.js.map +1 -1
- package/dist/scripts/test-spec-generator.d.ts.map +1 -1
- package/dist/scripts/test-spec-generator.js +1 -2
- package/dist/scripts/test-spec-generator.js.map +1 -1
- package/dist/scripts/utils/__tests__/config-loader.test.js +114 -1
- package/dist/scripts/utils/__tests__/config-loader.test.js.map +1 -1
- package/dist/scripts/utils/__tests__/config-validator.test.js +2 -0
- package/dist/scripts/utils/__tests__/config-validator.test.js.map +1 -1
- package/dist/scripts/utils/__tests__/env-config.test.js +0 -2
- package/dist/scripts/utils/__tests__/env-config.test.js.map +1 -1
- package/dist/scripts/utils/__tests__/project-meta.test.d.ts +6 -0
- package/dist/scripts/utils/__tests__/project-meta.test.d.ts.map +1 -0
- package/dist/scripts/utils/__tests__/project-meta.test.js +154 -0
- package/dist/scripts/utils/__tests__/project-meta.test.js.map +1 -0
- package/dist/scripts/utils/__tests__/security-validator.test.d.ts +6 -0
- package/dist/scripts/utils/__tests__/security-validator.test.d.ts.map +1 -0
- package/dist/scripts/utils/__tests__/security-validator.test.js +219 -0
- package/dist/scripts/utils/__tests__/security-validator.test.js.map +1 -0
- package/dist/scripts/utils/config-loader.d.ts +14 -3
- package/dist/scripts/utils/config-loader.d.ts.map +1 -1
- package/dist/scripts/utils/config-loader.js +284 -46
- package/dist/scripts/utils/config-loader.js.map +1 -1
- package/dist/scripts/utils/config-sections.d.ts +54 -0
- package/dist/scripts/utils/config-sections.d.ts.map +1 -0
- package/dist/scripts/utils/config-sections.js +178 -0
- package/dist/scripts/utils/config-sections.js.map +1 -0
- package/dist/scripts/utils/config-validator.d.ts +4 -0
- package/dist/scripts/utils/config-validator.d.ts.map +1 -1
- package/dist/scripts/utils/config-validator.js +57 -1
- package/dist/scripts/utils/config-validator.js.map +1 -1
- package/dist/scripts/utils/confluence-approval.d.ts.map +1 -1
- package/dist/scripts/utils/confluence-approval.js +5 -3
- package/dist/scripts/utils/confluence-approval.js.map +1 -1
- package/dist/scripts/utils/confluence-hierarchy.d.ts.map +1 -1
- package/dist/scripts/utils/confluence-hierarchy.js.map +1 -1
- package/dist/scripts/utils/env-config.d.ts +1 -1
- package/dist/scripts/utils/env-config.d.ts.map +1 -1
- package/dist/scripts/utils/env-config.js +2 -14
- package/dist/scripts/utils/env-config.js.map +1 -1
- package/dist/scripts/utils/interactive-helpers.d.ts +32 -0
- package/dist/scripts/utils/interactive-helpers.d.ts.map +1 -0
- package/dist/scripts/utils/interactive-helpers.js +92 -0
- package/dist/scripts/utils/interactive-helpers.js.map +1 -0
- package/dist/scripts/utils/jira-issue-type-fetcher.d.ts.map +1 -1
- package/dist/scripts/utils/jira-issue-type-fetcher.js +27 -18
- package/dist/scripts/utils/jira-issue-type-fetcher.js.map +1 -1
- package/dist/scripts/utils/project-meta.d.ts +9 -0
- package/dist/scripts/utils/project-meta.d.ts.map +1 -1
- package/dist/scripts/utils/project-meta.js +22 -0
- package/dist/scripts/utils/project-meta.js.map +1 -1
- package/dist/scripts/utils/release-notes-generator.d.ts.map +1 -1
- package/dist/scripts/utils/release-notes-generator.js +2 -1
- package/dist/scripts/utils/release-notes-generator.js.map +1 -1
- package/dist/scripts/utils/security-validator.d.ts +55 -0
- package/dist/scripts/utils/security-validator.d.ts.map +1 -0
- package/dist/scripts/utils/security-validator.js +232 -0
- package/dist/scripts/utils/security-validator.js.map +1 -0
- package/dist/scripts/utils/spec-updater.d.ts +19 -0
- package/dist/scripts/utils/spec-updater.d.ts.map +1 -1
- package/dist/scripts/utils/spec-updater.js.map +1 -1
- package/dist/scripts/utils/tasks-converter.d.ts.map +1 -1
- package/dist/scripts/utils/tasks-converter.js +2 -2
- package/dist/scripts/utils/tasks-converter.js.map +1 -1
- package/dist/scripts/utils/tasks-format-validator.d.ts.map +1 -1
- package/dist/scripts/utils/tasks-format-validator.js +0 -12
- package/dist/scripts/utils/tasks-format-validator.js.map +1 -1
- package/dist/scripts/utils/test-runner.d.ts.map +1 -1
- package/dist/scripts/utils/test-runner.js +3 -2
- package/dist/scripts/utils/test-runner.js.map +1 -1
- package/dist/scripts/validate-phase.d.ts +1 -1
- package/dist/scripts/validate-phase.d.ts.map +1 -1
- package/dist/scripts/validate-phase.js +12 -62
- package/dist/scripts/validate-phase.js.map +1 -1
- package/dist/scripts/workflow-orchestrator.d.ts.map +1 -1
- package/dist/scripts/workflow-orchestrator.js +11 -16
- package/dist/scripts/workflow-orchestrator.js.map +1 -1
- package/dist/src/__tests__/integration/setup/init.test.d.ts +5 -0
- package/dist/src/__tests__/integration/setup/init.test.d.ts.map +1 -0
- package/dist/src/__tests__/integration/setup/init.test.js +352 -0
- package/dist/src/__tests__/integration/setup/init.test.js.map +1 -0
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +67 -21
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/__tests__/init.test.d.ts +5 -0
- package/dist/src/commands/__tests__/init.test.d.ts.map +1 -0
- package/dist/src/commands/__tests__/init.test.js +255 -0
- package/dist/src/commands/__tests__/init.test.js.map +1 -0
- package/dist/src/commands/__tests__/migrate.test.d.ts +5 -0
- package/dist/src/commands/__tests__/migrate.test.d.ts.map +1 -0
- package/dist/src/commands/__tests__/migrate.test.js +216 -0
- package/dist/src/commands/__tests__/migrate.test.js.map +1 -0
- package/dist/src/commands/config-validate.d.ts +9 -0
- package/dist/src/commands/config-validate.d.ts.map +1 -0
- package/dist/src/commands/config-validate.js +90 -0
- package/dist/src/commands/config-validate.js.map +1 -0
- package/dist/src/commands/init.d.ts +29 -0
- package/dist/src/commands/init.d.ts.map +1 -0
- package/dist/src/commands/init.js +513 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/migrate.d.ts +25 -0
- package/dist/src/commands/migrate.d.ts.map +1 -0
- package/dist/src/commands/migrate.js +341 -0
- package/dist/src/commands/migrate.js.map +1 -0
- package/dist/src/commands/setup-existing.d.ts.map +1 -1
- package/dist/src/commands/setup-existing.js +0 -1
- package/dist/src/commands/setup-existing.js.map +1 -1
- package/dist/vitest.config.d.ts.map +1 -1
- package/dist/vitest.config.js +32 -8
- package/dist/vitest.config.js.map +1 -1
- package/docs/michi-development/design/config-unification.md +4789 -0
- package/docs/user-guide/getting-started/github-token-setup.md +2 -1
- package/docs/user-guide/getting-started/new-repository-setup.md +1 -1
- package/docs/user-guide/getting-started/quick-start.md +1 -1
- package/docs/user-guide/getting-started/setup.md +35 -14
- package/docs/user-guide/guides/customization.md +64 -11
- package/docs/user-guide/guides/workflow.md +35 -21
- package/docs/user-guide/hands-on/claude-agent-setup.md +2 -2
- package/docs/user-guide/hands-on/claude-setup.md +2 -2
- package/docs/user-guide/hands-on/cursor-setup.md +2 -2
- package/docs/user-guide/hands-on/workflow-walkthrough.md +4 -1
- package/docs/user-guide/reference/config.md +30 -5
- package/docs/user-guide/reference/quick-reference.md +68 -74
- package/docs/user-guide/testing/test-planning-flow.md +4 -0
- package/env.example +1 -1
- package/package.json +3 -5
- package/scripts/__tests__/spec-impl-workflow.test.ts +5 -2
- package/scripts/config/config-schema.ts +40 -0
- package/scripts/config-global.ts +160 -0
- package/scripts/confluence-sync.ts +91 -27
- package/scripts/jira-sync.ts +284 -218
- package/scripts/list-projects.ts +2 -2
- package/scripts/multi-project-estimate.ts +3 -3
- package/scripts/phase-runner.ts +391 -594
- package/scripts/pr-automation.ts +15 -5
- package/scripts/pre-flight-check.ts +20 -9
- package/scripts/pre-publish-check.sh +3 -34
- package/scripts/resource-dashboard.ts +4 -4
- package/scripts/spec-impl-workflow.ts +23 -7
- package/scripts/template/renderer.ts +1 -1
- package/scripts/test-interactive.ts +0 -19
- package/scripts/test-new-features.ts +10 -7
- package/scripts/test-npm-package.sh +3 -34
- package/scripts/test-spec-generator.ts +3 -7
- package/scripts/utils/__tests__/config-loader.test.ts +149 -0
- package/scripts/utils/__tests__/config-validator.test.ts +2 -0
- package/scripts/utils/__tests__/env-config.test.ts +0 -2
- package/scripts/utils/__tests__/project-meta.test.ts +192 -0
- package/scripts/utils/__tests__/security-validator.test.ts +272 -0
- package/scripts/utils/config-loader.ts +328 -68
- package/scripts/utils/config-sections.ts +316 -0
- package/scripts/utils/config-validator.ts +66 -1
- package/scripts/utils/confluence-approval.ts +8 -6
- package/scripts/utils/confluence-hierarchy.ts +27 -27
- package/scripts/utils/env-config.ts +2 -14
- package/scripts/utils/interactive-helpers.ts +135 -0
- package/scripts/utils/jira-issue-type-fetcher.ts +29 -21
- package/scripts/utils/project-meta.ts +27 -0
- package/scripts/utils/release-notes-generator.ts +3 -2
- package/scripts/utils/security-validator.ts +286 -0
- package/scripts/utils/spec-updater.ts +37 -15
- package/scripts/utils/tasks-converter.ts +4 -6
- package/scripts/utils/tasks-format-validator.ts +0 -13
- package/scripts/utils/test-runner.ts +4 -3
- package/scripts/validate-phase.ts +21 -80
- package/scripts/workflow-orchestrator.ts +16 -25
- package/templates/claude/commands/kiro/kiro-spec-impl.md +5 -1
- package/templates/claude/commands/kiro/kiro-spec-tasks.md +3 -1
- package/templates/claude/commands/michi/confluence-sync.md +8 -2
- package/templates/claude/commands/michi/design-review.md +4 -0
- package/templates/claude/commands/michi/e2e-plan.md +4 -0
- package/templates/claude/commands/michi/license-check.md +4 -0
- package/templates/claude/commands/michi/pr-resolve.md +4 -0
- package/templates/claude/commands/michi/project-switch.md +8 -2
- package/templates/claude/commands/michi/spec-design.md +78 -0
- package/templates/claude/commands/michi/spec-impl.md +716 -0
- package/templates/claude/commands/michi/test-planning.md +174 -0
- package/templates/claude/commands/michi/validate-design.md +58 -0
- package/templates/claude/commands/michi/version-audit.md +4 -0
- package/templates/claude-agent/commands/kiro/kiro-spec-impl.md +1 -1
- package/templates/cursor/commands/kiro/kiro-spec-impl.md +1 -1
- package/templates/michi/cc-sdd-overrides/README.md +8 -0
- package/templates/michi/cc-sdd-overrides/settings/rules/design-review-michi.md +53 -0
- package/dist/scripts/config-interactive.d.ts +0 -10
- package/dist/scripts/config-interactive.d.ts.map +0 -1
- package/dist/scripts/config-interactive.js +0 -372
- package/dist/scripts/config-interactive.js.map +0 -1
- package/dist/scripts/setup-existing-project.d.ts +0 -15
- package/dist/scripts/setup-existing-project.d.ts.map +0 -1
- package/dist/scripts/setup-existing-project.js +0 -455
- package/dist/scripts/setup-existing-project.js.map +0 -1
- package/dist/scripts/setup-interactive.d.ts +0 -10
- package/dist/scripts/setup-interactive.d.ts.map +0 -1
- package/dist/scripts/setup-interactive.js +0 -413
- package/dist/scripts/setup-interactive.js.map +0 -1
- package/scripts/config-interactive.ts +0 -550
- package/scripts/setup-existing-project.ts +0 -585
- package/scripts/setup-interactive.ts +0 -565
|
@@ -6,27 +6,53 @@
|
|
|
6
6
|
import { readFileSync, existsSync, statSync } from 'fs';
|
|
7
7
|
import { resolve, relative, isAbsolute } from 'path';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
|
-
import {
|
|
9
|
+
import { homedir } from 'os';
|
|
10
|
+
import { config, parse as dotenvParse } from 'dotenv';
|
|
10
11
|
import { AppConfigSchema, type AppConfig } from '../config/config-schema.js';
|
|
11
12
|
|
|
12
13
|
// 環境変数読み込み
|
|
13
14
|
config();
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* グローバル設定ファイルのパス定数
|
|
18
|
+
*/
|
|
19
|
+
const GLOBAL_CONFIG_DIR = '.michi';
|
|
20
|
+
const GLOBAL_CONFIG_FILE = 'config.json';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* グローバル設定ファイルのパスを取得
|
|
24
|
+
*/
|
|
25
|
+
export function getGlobalConfigPath(): string {
|
|
26
|
+
const home = homedir();
|
|
27
|
+
return resolve(home, GLOBAL_CONFIG_DIR, GLOBAL_CONFIG_FILE);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* グローバル.envファイルのパスを取得
|
|
32
|
+
*/
|
|
33
|
+
export function getGlobalEnvPath(): string {
|
|
34
|
+
const home = process.env.HOME || homedir();
|
|
35
|
+
return resolve(home, GLOBAL_CONFIG_DIR, '.env');
|
|
36
|
+
}
|
|
37
|
+
|
|
15
38
|
/**
|
|
16
39
|
* 深いマージ(Deep Merge)
|
|
17
40
|
* オブジェクトを再帰的にマージする
|
|
18
41
|
*/
|
|
19
|
-
function deepMerge<T extends Record<string,
|
|
42
|
+
function deepMerge<T extends Record<string, unknown>>(target: T, source: Partial<T>): T {
|
|
20
43
|
const result = { ...target };
|
|
21
|
-
|
|
44
|
+
|
|
22
45
|
for (const key in source) {
|
|
23
46
|
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
24
|
-
result[key] = deepMerge(
|
|
47
|
+
result[key] = deepMerge(
|
|
48
|
+
(result[key] || {}) as Record<string, unknown>,
|
|
49
|
+
source[key] as Record<string, unknown>
|
|
50
|
+
) as T[Extract<keyof T, string>];
|
|
25
51
|
} else if (source[key] !== undefined) {
|
|
26
52
|
result[key] = source[key] as T[Extract<keyof T, string>];
|
|
27
53
|
}
|
|
28
54
|
}
|
|
29
|
-
|
|
55
|
+
|
|
30
56
|
return result;
|
|
31
57
|
}
|
|
32
58
|
|
|
@@ -60,7 +86,7 @@ function expandEnvVars(str: string): string {
|
|
|
60
86
|
* 設定オブジェクト内の文字列値を環境変数で展開
|
|
61
87
|
* 循環参照を防ぐため、処理済みオブジェクトを追跡
|
|
62
88
|
*/
|
|
63
|
-
function expandEnvVarsInConfig(config:
|
|
89
|
+
function expandEnvVarsInConfig(config: unknown, visited: WeakSet<object> = new WeakSet()): unknown {
|
|
64
90
|
if (typeof config === 'string') {
|
|
65
91
|
return expandEnvVars(config);
|
|
66
92
|
}
|
|
@@ -77,9 +103,9 @@ function expandEnvVarsInConfig(config: any, visited: WeakSet<object> = new WeakS
|
|
|
77
103
|
}
|
|
78
104
|
|
|
79
105
|
visited.add(config);
|
|
80
|
-
const result:
|
|
81
|
-
for (const key in config) {
|
|
82
|
-
result[key] = expandEnvVarsInConfig(config[key], visited);
|
|
106
|
+
const result: Record<string, unknown> = {};
|
|
107
|
+
for (const key in config as Record<string, unknown>) {
|
|
108
|
+
result[key] = expandEnvVarsInConfig((config as Record<string, unknown>)[key], visited);
|
|
83
109
|
}
|
|
84
110
|
visited.delete(config);
|
|
85
111
|
return result;
|
|
@@ -113,7 +139,8 @@ function loadDefaultConfig(): AppConfig {
|
|
|
113
139
|
return AppConfigSchema.parse(expanded);
|
|
114
140
|
} catch (error) {
|
|
115
141
|
if (error instanceof SyntaxError) {
|
|
116
|
-
|
|
142
|
+
// SyntaxErrorには標準的なlineやcolumnプロパティはないため、messageのみ使用
|
|
143
|
+
throw new Error(`Invalid JSON in default config file ${defaultConfigPath}: ${error.message}`);
|
|
117
144
|
}
|
|
118
145
|
if (error instanceof Error && error.name === 'ZodError') {
|
|
119
146
|
throw new Error(`Default config validation failed: ${error.message}\nFile: ${defaultConfigPath}`);
|
|
@@ -166,23 +193,23 @@ function resolveConfigPath(projectRoot: string): string {
|
|
|
166
193
|
*/
|
|
167
194
|
function loadProjectConfig(projectRoot: string = process.cwd()): Partial<AppConfig> | null {
|
|
168
195
|
const projectConfigPath = resolveConfigPath(projectRoot);
|
|
169
|
-
|
|
196
|
+
|
|
170
197
|
// パストラバーサル対策: パスを検証
|
|
171
198
|
if (!validateConfigPath(projectConfigPath, projectRoot)) {
|
|
172
199
|
throw new Error(`Invalid config path: ${projectConfigPath} is outside project root`);
|
|
173
200
|
}
|
|
174
|
-
|
|
201
|
+
|
|
175
202
|
if (!existsSync(projectConfigPath)) {
|
|
176
203
|
return null;
|
|
177
204
|
}
|
|
178
|
-
|
|
205
|
+
|
|
179
206
|
try {
|
|
180
207
|
const content = readFileSync(projectConfigPath, 'utf-8');
|
|
181
208
|
const parsed = JSON.parse(content);
|
|
182
|
-
|
|
209
|
+
|
|
183
210
|
// 環境変数を展開
|
|
184
211
|
const expanded = expandEnvVarsInConfig(parsed);
|
|
185
|
-
|
|
212
|
+
|
|
186
213
|
// 部分的な設定なので、スキーマで厳密にバリデーションしない
|
|
187
214
|
// ただし、存在するキーについては型チェック
|
|
188
215
|
return expanded as Partial<AppConfig>;
|
|
@@ -198,62 +225,199 @@ function loadProjectConfig(projectRoot: string = process.cwd()): Partial<AppConf
|
|
|
198
225
|
}
|
|
199
226
|
|
|
200
227
|
/**
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
* マージ順序:
|
|
204
|
-
* 1. デフォルト設定
|
|
205
|
-
* 2. プロジェクト固有設定(上書き)
|
|
206
|
-
* 3. 環境変数(最終上書き、既存の動作を維持)
|
|
228
|
+
* グローバル設定を読み込む
|
|
207
229
|
*/
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
// spacesオブジェクトを確実に作成
|
|
231
|
-
if (!mergedConfig.confluence.spaces) {
|
|
232
|
-
mergedConfig.confluence.spaces = {};
|
|
233
|
-
}
|
|
234
|
-
// 各フィールドを個別にチェックし、未定義のフィールドのみ環境変数で設定
|
|
235
|
-
// 既に定義されている値は変更しない
|
|
236
|
-
if (!mergedConfig.confluence.spaces.requirements) {
|
|
237
|
-
mergedConfig.confluence.spaces.requirements = process.env.CONFLUENCE_PRD_SPACE;
|
|
238
|
-
}
|
|
239
|
-
if (!mergedConfig.confluence.spaces.design) {
|
|
240
|
-
mergedConfig.confluence.spaces.design = process.env.CONFLUENCE_PRD_SPACE;
|
|
230
|
+
function loadGlobalConfig(): Partial<AppConfig> | null {
|
|
231
|
+
const globalConfigPath = getGlobalConfigPath();
|
|
232
|
+
|
|
233
|
+
if (!existsSync(globalConfigPath)) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const content = readFileSync(globalConfigPath, 'utf-8');
|
|
239
|
+
const parsed = JSON.parse(content);
|
|
240
|
+
|
|
241
|
+
// 環境変数を展開
|
|
242
|
+
const expanded = expandEnvVarsInConfig(parsed);
|
|
243
|
+
|
|
244
|
+
return expanded as Partial<AppConfig>;
|
|
245
|
+
} catch (error) {
|
|
246
|
+
if (error instanceof SyntaxError) {
|
|
247
|
+
console.warn(`⚠️ Invalid JSON in global config ${globalConfigPath}: ${error.message}`);
|
|
248
|
+
} else {
|
|
249
|
+
console.warn(`⚠️ Failed to load global config: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
241
250
|
}
|
|
242
|
-
|
|
243
|
-
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* グローバル.envを読み込み
|
|
257
|
+
* ~/.michi/.env から組織共通の環境変数を読み込む
|
|
258
|
+
*/
|
|
259
|
+
function loadGlobalEnv(): Record<string, string> {
|
|
260
|
+
const globalEnvPath = getGlobalEnvPath();
|
|
261
|
+
|
|
262
|
+
if (!existsSync(globalEnvPath)) {
|
|
263
|
+
return {};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
const content = readFileSync(globalEnvPath, 'utf-8');
|
|
268
|
+
return dotenvParse(content);
|
|
269
|
+
} catch (error) {
|
|
270
|
+
console.warn(`⚠️ Failed to load global env from ${globalEnvPath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
271
|
+
return {};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* プロジェクト.envを読み込み
|
|
277
|
+
* .env からプロジェクト固有の環境変数を読み込む
|
|
278
|
+
*/
|
|
279
|
+
function loadProjectEnv(projectRoot: string): Record<string, string> {
|
|
280
|
+
const projectEnvPath = resolve(projectRoot, '.env');
|
|
281
|
+
|
|
282
|
+
if (!existsSync(projectEnvPath)) {
|
|
283
|
+
return {};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
const content = readFileSync(projectEnvPath, 'utf-8');
|
|
288
|
+
return dotenvParse(content);
|
|
289
|
+
} catch (error) {
|
|
290
|
+
console.warn(`⚠️ Failed to load project env from ${projectEnvPath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
291
|
+
return {};
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* プロジェクトメタデータを読み込み
|
|
297
|
+
* .kiro/project.json からプロジェクト情報を読み込む
|
|
298
|
+
*/
|
|
299
|
+
function loadProjectMetadata(projectRoot: string): Partial<AppConfig> | null {
|
|
300
|
+
const projectJsonPath = resolve(projectRoot, '.kiro/project.json');
|
|
301
|
+
|
|
302
|
+
if (!existsSync(projectJsonPath)) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
const content = readFileSync(projectJsonPath, 'utf-8');
|
|
308
|
+
const meta = JSON.parse(content);
|
|
309
|
+
|
|
310
|
+
// project フィールドとして返す
|
|
311
|
+
return { project: meta } as Partial<AppConfig>;
|
|
312
|
+
} catch (error) {
|
|
313
|
+
if (error instanceof SyntaxError) {
|
|
314
|
+
console.warn(`⚠️ Invalid JSON in ${projectJsonPath}: ${error.message}`);
|
|
315
|
+
} else {
|
|
316
|
+
console.warn(`⚠️ Failed to load project metadata from ${projectJsonPath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
244
317
|
}
|
|
318
|
+
return null;
|
|
245
319
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* 環境変数から設定へのマッピング
|
|
324
|
+
*/
|
|
325
|
+
const ENV_TO_CONFIG_MAPPING: Record<string, string> = {
|
|
326
|
+
'ATLASSIAN_URL': 'atlassian.url',
|
|
327
|
+
'ATLASSIAN_EMAIL': 'atlassian.email',
|
|
328
|
+
'ATLASSIAN_API_TOKEN': 'atlassian.apiToken',
|
|
329
|
+
'GITHUB_ORG': 'github.org',
|
|
330
|
+
'GITHUB_TOKEN': 'github.token',
|
|
331
|
+
'CONFLUENCE_PRD_SPACE': 'confluence.spaces.requirements',
|
|
332
|
+
'CONFLUENCE_QA_SPACE': 'confluence.spaces.qa',
|
|
333
|
+
'CONFLUENCE_RELEASE_SPACE': 'confluence.spaces.release',
|
|
334
|
+
'JIRA_ISSUE_TYPE_STORY': 'jira.issueTypes.story',
|
|
335
|
+
'JIRA_ISSUE_TYPE_SUBTASK': 'jira.issueTypes.subtask',
|
|
336
|
+
'JIRA_PROJECT_KEYS': 'jira.projectKeys',
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* ドットパスでオブジェクトの値を設定
|
|
341
|
+
* 例: setValueByPath(obj, 'a.b.c', value) => obj.a.b.c = value
|
|
342
|
+
*/
|
|
343
|
+
function setValueByPath(obj: Record<string, unknown>, path: string, value: string): void {
|
|
344
|
+
const keys = path.split('.');
|
|
345
|
+
let current: Record<string, unknown> = obj;
|
|
346
|
+
|
|
347
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
348
|
+
const key = keys[i];
|
|
349
|
+
if (!current[key] || typeof current[key] !== 'object') {
|
|
350
|
+
current[key] = {};
|
|
251
351
|
}
|
|
252
|
-
|
|
253
|
-
|
|
352
|
+
current = current[key] as Record<string, unknown>;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
current[keys[keys.length - 1]] = value;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* 環境変数を設定オブジェクトに適用
|
|
360
|
+
*/
|
|
361
|
+
function applyEnvVarsToConfig(
|
|
362
|
+
config: AppConfig,
|
|
363
|
+
envVars: Record<string, string>
|
|
364
|
+
): AppConfig {
|
|
365
|
+
const result = { ...config } as Record<string, unknown>;
|
|
366
|
+
|
|
367
|
+
for (const [envKey, configPath] of Object.entries(ENV_TO_CONFIG_MAPPING)) {
|
|
368
|
+
if (envVars[envKey]) {
|
|
369
|
+
setValueByPath(result, configPath, envVars[envKey]);
|
|
254
370
|
}
|
|
255
371
|
}
|
|
256
|
-
|
|
372
|
+
|
|
373
|
+
return result as AppConfig;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* 設定を読み込んでマージ
|
|
378
|
+
*
|
|
379
|
+
* マージ順序(優先度: 低 → 高):
|
|
380
|
+
* 1. デフォルト設定
|
|
381
|
+
* 2. グローバル.env(~/.michi/.env)
|
|
382
|
+
* 3. グローバル設定(~/.michi/config.json)
|
|
383
|
+
* 4. プロジェクトメタデータ(.kiro/project.json)
|
|
384
|
+
* 5. プロジェクト設定(.michi/config.json)
|
|
385
|
+
* 6. プロジェクト.env(.env)- 最高優先度
|
|
386
|
+
*/
|
|
387
|
+
export function loadConfig(projectRoot: string = process.cwd()): AppConfig {
|
|
388
|
+
// 1. デフォルト設定を読み込み(最低優先度)
|
|
389
|
+
let mergedConfig: AppConfig = loadDefaultConfig();
|
|
390
|
+
|
|
391
|
+
// 2. グローバル.envを読み込み
|
|
392
|
+
const globalEnvVars = loadGlobalEnv();
|
|
393
|
+
if (Object.keys(globalEnvVars).length > 0) {
|
|
394
|
+
mergedConfig = applyEnvVarsToConfig(mergedConfig, globalEnvVars);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// 3. グローバル設定を読み込み
|
|
398
|
+
const globalConfig = loadGlobalConfig();
|
|
399
|
+
if (globalConfig) {
|
|
400
|
+
mergedConfig = deepMerge(mergedConfig, globalConfig);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// 4. プロジェクトメタデータを読み込み
|
|
404
|
+
const projectMeta = loadProjectMetadata(projectRoot);
|
|
405
|
+
if (projectMeta) {
|
|
406
|
+
mergedConfig = deepMerge(mergedConfig, projectMeta);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// 5. プロジェクト固有設定を読み込み
|
|
410
|
+
const projectConfig = loadProjectConfig(projectRoot);
|
|
411
|
+
if (projectConfig) {
|
|
412
|
+
mergedConfig = deepMerge(mergedConfig, projectConfig);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// 6. プロジェクト.envを読み込み(最高優先度)
|
|
416
|
+
const projectEnvVars = loadProjectEnv(projectRoot);
|
|
417
|
+
if (Object.keys(projectEnvVars).length > 0) {
|
|
418
|
+
mergedConfig = applyEnvVarsToConfig(mergedConfig, projectEnvVars);
|
|
419
|
+
}
|
|
420
|
+
|
|
257
421
|
// スキーマで最終バリデーション
|
|
258
422
|
return AppConfigSchema.parse(mergedConfig);
|
|
259
423
|
}
|
|
@@ -266,14 +430,22 @@ let cachedConfig: AppConfig | null = null;
|
|
|
266
430
|
let cachedProjectRoot: string | null = null;
|
|
267
431
|
let cachedConfigMtime: number | null = null;
|
|
268
432
|
let cachedDefaultConfigMtime: number | null = null;
|
|
433
|
+
let cachedGlobalConfigMtime: number | null = null;
|
|
434
|
+
let cachedGlobalEnvMtime: number | null = null;
|
|
435
|
+
let cachedProjectMetaMtime: number | null = null;
|
|
436
|
+
let cachedProjectEnvMtime: number | null = null;
|
|
269
437
|
|
|
270
438
|
export function getConfig(projectRoot: string = process.cwd()): AppConfig {
|
|
271
439
|
const projectConfigPath = resolveConfigPath(projectRoot);
|
|
440
|
+
const globalConfigPath = getGlobalConfigPath();
|
|
441
|
+
const globalEnvPath = getGlobalEnvPath();
|
|
442
|
+
const projectMetaPath = resolve(projectRoot, '.kiro/project.json');
|
|
443
|
+
const projectEnvPath = resolve(projectRoot, '.env');
|
|
272
444
|
const currentFileUrl = import.meta.url;
|
|
273
445
|
const currentFilePath = fileURLToPath(currentFileUrl);
|
|
274
446
|
const currentDir = resolve(currentFilePath, '..');
|
|
275
447
|
const defaultConfigPath = resolve(currentDir, '../config/default-config.json');
|
|
276
|
-
|
|
448
|
+
|
|
277
449
|
// デフォルト設定ファイルの更新時刻をチェック
|
|
278
450
|
let defaultConfigChanged = false;
|
|
279
451
|
try {
|
|
@@ -295,7 +467,27 @@ export function getConfig(projectRoot: string = process.cwd()): AppConfig {
|
|
|
295
467
|
defaultConfigChanged = true;
|
|
296
468
|
cachedDefaultConfigMtime = null;
|
|
297
469
|
}
|
|
298
|
-
|
|
470
|
+
|
|
471
|
+
// グローバル設定ファイルの更新時刻をチェック
|
|
472
|
+
let globalConfigChanged = false;
|
|
473
|
+
try {
|
|
474
|
+
if (existsSync(globalConfigPath)) {
|
|
475
|
+
const globalStats = statSync(globalConfigPath);
|
|
476
|
+
if (cachedGlobalConfigMtime !== globalStats.mtimeMs) {
|
|
477
|
+
globalConfigChanged = true;
|
|
478
|
+
cachedGlobalConfigMtime = globalStats.mtimeMs;
|
|
479
|
+
}
|
|
480
|
+
} else {
|
|
481
|
+
if (cachedGlobalConfigMtime !== null) {
|
|
482
|
+
globalConfigChanged = true;
|
|
483
|
+
cachedGlobalConfigMtime = null;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
} catch {
|
|
487
|
+
globalConfigChanged = true;
|
|
488
|
+
cachedGlobalConfigMtime = null;
|
|
489
|
+
}
|
|
490
|
+
|
|
299
491
|
// プロジェクト設定ファイルの更新時刻をチェック
|
|
300
492
|
let projectConfigChanged = false;
|
|
301
493
|
try {
|
|
@@ -317,13 +509,77 @@ export function getConfig(projectRoot: string = process.cwd()): AppConfig {
|
|
|
317
509
|
projectConfigChanged = true;
|
|
318
510
|
cachedConfigMtime = null;
|
|
319
511
|
}
|
|
320
|
-
|
|
512
|
+
|
|
513
|
+
// グローバル.envファイルの更新時刻をチェック
|
|
514
|
+
let globalEnvChanged = false;
|
|
515
|
+
try {
|
|
516
|
+
if (existsSync(globalEnvPath)) {
|
|
517
|
+
const globalEnvStats = statSync(globalEnvPath);
|
|
518
|
+
if (cachedGlobalEnvMtime !== globalEnvStats.mtimeMs) {
|
|
519
|
+
globalEnvChanged = true;
|
|
520
|
+
cachedGlobalEnvMtime = globalEnvStats.mtimeMs;
|
|
521
|
+
}
|
|
522
|
+
} else {
|
|
523
|
+
if (cachedGlobalEnvMtime !== null) {
|
|
524
|
+
globalEnvChanged = true;
|
|
525
|
+
cachedGlobalEnvMtime = null;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
} catch {
|
|
529
|
+
globalEnvChanged = true;
|
|
530
|
+
cachedGlobalEnvMtime = null;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// プロジェクトメタデータファイルの更新時刻をチェック
|
|
534
|
+
let projectMetaChanged = false;
|
|
535
|
+
try {
|
|
536
|
+
if (existsSync(projectMetaPath)) {
|
|
537
|
+
const projectMetaStats = statSync(projectMetaPath);
|
|
538
|
+
if (cachedProjectMetaMtime !== projectMetaStats.mtimeMs) {
|
|
539
|
+
projectMetaChanged = true;
|
|
540
|
+
cachedProjectMetaMtime = projectMetaStats.mtimeMs;
|
|
541
|
+
}
|
|
542
|
+
} else {
|
|
543
|
+
if (cachedProjectMetaMtime !== null) {
|
|
544
|
+
projectMetaChanged = true;
|
|
545
|
+
cachedProjectMetaMtime = null;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
} catch {
|
|
549
|
+
projectMetaChanged = true;
|
|
550
|
+
cachedProjectMetaMtime = null;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// プロジェクト.envファイルの更新時刻をチェック
|
|
554
|
+
let projectEnvChanged = false;
|
|
555
|
+
try {
|
|
556
|
+
if (existsSync(projectEnvPath)) {
|
|
557
|
+
const projectEnvStats = statSync(projectEnvPath);
|
|
558
|
+
if (cachedProjectEnvMtime !== projectEnvStats.mtimeMs) {
|
|
559
|
+
projectEnvChanged = true;
|
|
560
|
+
cachedProjectEnvMtime = projectEnvStats.mtimeMs;
|
|
561
|
+
}
|
|
562
|
+
} else {
|
|
563
|
+
if (cachedProjectEnvMtime !== null) {
|
|
564
|
+
projectEnvChanged = true;
|
|
565
|
+
cachedProjectEnvMtime = null;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
} catch {
|
|
569
|
+
projectEnvChanged = true;
|
|
570
|
+
cachedProjectEnvMtime = null;
|
|
571
|
+
}
|
|
572
|
+
|
|
321
573
|
// キャッシュが有効で、設定ファイルが変更されていない場合はキャッシュを返す
|
|
322
574
|
if (
|
|
323
575
|
cachedConfig &&
|
|
324
576
|
cachedProjectRoot === projectRoot &&
|
|
325
577
|
!defaultConfigChanged &&
|
|
326
|
-
!
|
|
578
|
+
!globalConfigChanged &&
|
|
579
|
+
!globalEnvChanged &&
|
|
580
|
+
!projectConfigChanged &&
|
|
581
|
+
!projectMetaChanged &&
|
|
582
|
+
!projectEnvChanged
|
|
327
583
|
) {
|
|
328
584
|
return cachedConfig;
|
|
329
585
|
}
|
|
@@ -343,6 +599,10 @@ export function clearConfigCache(): void {
|
|
|
343
599
|
cachedProjectRoot = null;
|
|
344
600
|
cachedConfigMtime = null;
|
|
345
601
|
cachedDefaultConfigMtime = null;
|
|
602
|
+
cachedGlobalConfigMtime = null;
|
|
603
|
+
cachedGlobalEnvMtime = null;
|
|
604
|
+
cachedProjectMetaMtime = null;
|
|
605
|
+
cachedProjectEnvMtime = null;
|
|
346
606
|
}
|
|
347
607
|
|
|
348
608
|
/**
|