myaidev-method 0.2.22 → 0.2.24-1
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/USER_GUIDE.md +453 -48
- package/bin/cli.js +236 -38
- package/content-rules.example.md +80 -0
- package/dist/mcp/mcp-launcher.js +237 -0
- package/dist/server/.tsbuildinfo +1 -1
- package/dist/server/auth/layers.d.ts +1 -1
- package/dist/server/auth/services/AuthService.d.ts +1 -1
- package/dist/server/auth/services/TokenService.js.map +1 -1
- package/dist/server/auth/services/example.d.ts +5 -5
- package/package.json +22 -17
- package/src/config/workflows.js +28 -44
- package/src/index.js +21 -8
- package/src/lib/ascii-banner.js +214 -0
- package/src/lib/config-manager.js +470 -0
- package/src/lib/content-generator.js +427 -0
- package/src/lib/html-conversion-utils.js +843 -0
- package/src/lib/seo-optimizer.js +515 -0
- package/src/lib/update-manager.js +2 -1
- package/src/lib/visual-config-utils.js +321 -295
- package/src/lib/visual-generation-utils.js +1000 -811
- package/src/lib/wordpress-client.js +633 -0
- package/src/lib/workflow-installer.js +3 -3
- package/src/scripts/configure-wordpress-mcp.js +8 -3
- package/src/scripts/generate-visual-cli.js +365 -235
- package/src/scripts/html-conversion-cli.js +526 -0
- package/src/scripts/init/configure.js +436 -0
- package/src/scripts/init/install.js +460 -0
- package/src/scripts/ping.js +250 -0
- package/src/scripts/utils/file-utils.js +404 -0
- package/src/scripts/utils/logger.js +300 -0
- package/src/scripts/utils/write-content.js +293 -0
- package/src/scripts/wordpress/publish-to-wordpress.js +165 -0
- package/src/server/auth/services/TokenService.ts +1 -1
- package/src/templates/claude/agents/content-rules-setup.md +657 -0
- package/src/templates/claude/agents/content-writer.md +328 -1
- package/src/templates/claude/agents/visual-content-generator.md +311 -8
- package/src/templates/claude/commands/myai-configure.md +1 -1
- package/src/templates/claude/commands/myai-content-rules-setup.md +204 -0
- package/src/templates/claude/commands/myai-convert-html.md +186 -0
- package/src/templates/codex/commands/myai-content-rules-setup.md +85 -0
- package/src/templates/diagrams/architecture.d2 +52 -0
- package/src/templates/diagrams/flowchart.d2 +42 -0
- package/src/templates/diagrams/sequence.d2 +47 -0
- package/src/templates/docs/content-creation-guide.md +164 -0
- package/src/templates/docs/deployment-guide.md +336 -0
- package/src/templates/docs/visual-generation-guide.md +248 -0
- package/src/templates/docs/wordpress-publishing-guide.md +208 -0
- package/src/templates/gemini/commands/myai-content-rules-setup.toml +57 -0
- package/src/templates/infographics/comparison-table.html +347 -0
- package/src/templates/infographics/data-chart.html +268 -0
- package/src/templates/infographics/process-flow.html +365 -0
- package/.claude/mcp/sparc-orchestrator-server.js +0 -607
- package/.claude/mcp/wordpress-server.js +0 -1277
- package/src/agents/content-writer-prompt.md +0 -164
- package/src/agents/content-writer.json +0 -70
- package/src/templates/claude/mcp_config.json +0 -74
- package/src/templates/claude/slash_commands.json +0 -166
- package/src/templates/scripts/configure-wordpress-mcp.js +0 -181
- /package/src/scripts/{wordpress-health-check.js → wordpress/wordpress-health-check.js} +0 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Manager
|
|
3
|
+
* Central configuration management for MyAIDev Method
|
|
4
|
+
* Handles loading, validating, and persisting configurations
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import fs from 'fs-extra';
|
|
9
|
+
|
|
10
|
+
// Configuration schemas for validation
|
|
11
|
+
export const SCHEMAS = {
|
|
12
|
+
wordpress: {
|
|
13
|
+
required: ['WORDPRESS_URL', 'WORDPRESS_USERNAME', 'WORDPRESS_APP_PASSWORD'],
|
|
14
|
+
optional: ['WORDPRESS_DEFAULT_STATUS', 'WORDPRESS_DEFAULT_CATEGORIES'],
|
|
15
|
+
defaults: {
|
|
16
|
+
WORDPRESS_DEFAULT_STATUS: 'draft'
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
openstack: {
|
|
20
|
+
required: ['OS_AUTH_URL', 'OS_USERNAME', 'OS_PASSWORD', 'OS_PROJECT_NAME'],
|
|
21
|
+
optional: ['OS_REGION_NAME', 'OS_USER_DOMAIN_NAME', 'OS_PROJECT_DOMAIN_NAME'],
|
|
22
|
+
defaults: {
|
|
23
|
+
OS_USER_DOMAIN_NAME: 'Default',
|
|
24
|
+
OS_PROJECT_DOMAIN_NAME: 'Default'
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
content: {
|
|
28
|
+
required: [],
|
|
29
|
+
optional: ['DEFAULT_WORD_COUNT', 'DEFAULT_TONE', 'CONTENT_OUTPUT_DIR'],
|
|
30
|
+
defaults: {
|
|
31
|
+
DEFAULT_WORD_COUNT: '1500',
|
|
32
|
+
DEFAULT_TONE: 'professional',
|
|
33
|
+
CONTENT_OUTPUT_DIR: './content'
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
defaults: {
|
|
37
|
+
required: [],
|
|
38
|
+
optional: ['LOG_LEVEL', 'CLI_TYPE'],
|
|
39
|
+
defaults: {
|
|
40
|
+
LOG_LEVEL: 'INFO',
|
|
41
|
+
CLI_TYPE: 'claude'
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Configuration Manager Class
|
|
48
|
+
*/
|
|
49
|
+
export class ConfigManager {
|
|
50
|
+
constructor(projectRoot = process.cwd()) {
|
|
51
|
+
this.projectRoot = projectRoot;
|
|
52
|
+
this.configCache = null;
|
|
53
|
+
this.lastLoad = null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get path to .env file
|
|
58
|
+
* @returns {string}
|
|
59
|
+
*/
|
|
60
|
+
getEnvPath() {
|
|
61
|
+
return path.join(this.projectRoot, '.env');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get path to .claude/config directory
|
|
66
|
+
* @returns {string}
|
|
67
|
+
*/
|
|
68
|
+
getClaudeConfigDir() {
|
|
69
|
+
return path.join(this.projectRoot, '.claude', 'config');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Load all configurations from various sources
|
|
74
|
+
* @param {boolean} [force=false] - Force reload even if cached
|
|
75
|
+
* @returns {Promise<Object>} Merged configuration
|
|
76
|
+
*/
|
|
77
|
+
async loadAll(force = false) {
|
|
78
|
+
// Return cached config if recent (within 5 seconds)
|
|
79
|
+
if (!force && this.configCache && this.lastLoad) {
|
|
80
|
+
const age = Date.now() - this.lastLoad;
|
|
81
|
+
if (age < 5000) {
|
|
82
|
+
return this.configCache;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const config = {};
|
|
87
|
+
|
|
88
|
+
// 1. Load from .env file
|
|
89
|
+
const envConfig = await this.loadEnvFile();
|
|
90
|
+
Object.assign(config, envConfig);
|
|
91
|
+
|
|
92
|
+
// 2. Load from .claude/config/*.json files
|
|
93
|
+
const claudeConfigs = await this.loadClaudeConfigs();
|
|
94
|
+
Object.assign(config, claudeConfigs);
|
|
95
|
+
|
|
96
|
+
// 3. Apply defaults for any missing values
|
|
97
|
+
this.applyDefaults(config);
|
|
98
|
+
|
|
99
|
+
// Cache the result
|
|
100
|
+
this.configCache = config;
|
|
101
|
+
this.lastLoad = Date.now();
|
|
102
|
+
|
|
103
|
+
return config;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Load configuration from .env file
|
|
108
|
+
* @returns {Promise<Object>}
|
|
109
|
+
*/
|
|
110
|
+
async loadEnvFile() {
|
|
111
|
+
const envPath = this.getEnvPath();
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const exists = await fs.pathExists(envPath);
|
|
115
|
+
if (!exists) {
|
|
116
|
+
return {};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const content = await fs.readFile(envPath, 'utf-8');
|
|
120
|
+
return this.parseEnvContent(content);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.warn(`Warning: Failed to load .env file: ${error.message}`);
|
|
123
|
+
return {};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Load configuration files from .claude/config/
|
|
129
|
+
* @returns {Promise<Object>}
|
|
130
|
+
*/
|
|
131
|
+
async loadClaudeConfigs() {
|
|
132
|
+
const configDir = this.getClaudeConfigDir();
|
|
133
|
+
const merged = {};
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const exists = await fs.pathExists(configDir);
|
|
137
|
+
if (!exists) {
|
|
138
|
+
return merged;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const files = await fs.readdir(configDir);
|
|
142
|
+
const jsonFiles = files.filter(f => f.endsWith('.json'));
|
|
143
|
+
|
|
144
|
+
for (const file of jsonFiles) {
|
|
145
|
+
const filePath = path.join(configDir, file);
|
|
146
|
+
try {
|
|
147
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
148
|
+
const data = JSON.parse(content);
|
|
149
|
+
Object.assign(merged, data);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
console.warn(`Warning: Failed to load config file ${file}: ${err.message}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return merged;
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.warn(`Warning: Failed to load claude configs: ${error.message}`);
|
|
158
|
+
return merged;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Apply default values for missing configuration
|
|
164
|
+
* @param {Object} config - Configuration object to modify
|
|
165
|
+
*/
|
|
166
|
+
applyDefaults(config) {
|
|
167
|
+
for (const schema of Object.values(SCHEMAS)) {
|
|
168
|
+
for (const [key, value] of Object.entries(schema.defaults || {})) {
|
|
169
|
+
if (config[key] === undefined) {
|
|
170
|
+
config[key] = value;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get a specific configuration value
|
|
178
|
+
* @param {string} key - Configuration key
|
|
179
|
+
* @param {any} [defaultValue] - Default value if not found
|
|
180
|
+
* @returns {Promise<any>}
|
|
181
|
+
*/
|
|
182
|
+
async getConfig(key, defaultValue = undefined) {
|
|
183
|
+
const config = await this.loadAll();
|
|
184
|
+
return config[key] !== undefined ? config[key] : defaultValue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Save workflow-specific configuration
|
|
189
|
+
* @param {string} name - Workflow/config name (e.g., 'wordpress', 'openstack')
|
|
190
|
+
* @param {Object} config - Configuration values
|
|
191
|
+
* @returns {Promise<boolean>}
|
|
192
|
+
*/
|
|
193
|
+
async saveWorkflowConfig(name, config) {
|
|
194
|
+
const configDir = this.getClaudeConfigDir();
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
await fs.ensureDir(configDir);
|
|
198
|
+
|
|
199
|
+
const configPath = path.join(configDir, `${name}.json`);
|
|
200
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
201
|
+
|
|
202
|
+
// Invalidate cache
|
|
203
|
+
this.configCache = null;
|
|
204
|
+
|
|
205
|
+
return true;
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error(`Failed to save ${name} config: ${error.message}`);
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Save configuration to .env file
|
|
214
|
+
* @param {Object} config - Configuration to save
|
|
215
|
+
* @param {boolean} [merge=true] - Merge with existing .env
|
|
216
|
+
* @returns {Promise<boolean>}
|
|
217
|
+
*/
|
|
218
|
+
async saveEnvConfig(config, merge = true) {
|
|
219
|
+
const envPath = this.getEnvPath();
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
let existing = {};
|
|
223
|
+
if (merge) {
|
|
224
|
+
existing = await this.loadEnvFile();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const merged = { ...existing, ...config };
|
|
228
|
+
const content = this.formatEnvContent(merged);
|
|
229
|
+
|
|
230
|
+
await fs.writeFile(envPath, content, 'utf-8');
|
|
231
|
+
|
|
232
|
+
// Invalidate cache
|
|
233
|
+
this.configCache = null;
|
|
234
|
+
|
|
235
|
+
return true;
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.error(`Failed to save .env config: ${error.message}`);
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Validate configuration against a schema
|
|
244
|
+
* @param {Object} config - Configuration to validate
|
|
245
|
+
* @param {string} schemaName - Schema name ('wordpress', 'openstack', etc.)
|
|
246
|
+
* @returns {{valid: boolean, missing: string[], warnings: string[]}}
|
|
247
|
+
*/
|
|
248
|
+
validateConfig(config, schemaName) {
|
|
249
|
+
const schema = SCHEMAS[schemaName];
|
|
250
|
+
if (!schema) {
|
|
251
|
+
return { valid: false, missing: [], warnings: [`Unknown schema: ${schemaName}`] };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const missing = [];
|
|
255
|
+
const warnings = [];
|
|
256
|
+
|
|
257
|
+
// Check required fields
|
|
258
|
+
for (const field of schema.required || []) {
|
|
259
|
+
if (!config[field] || config[field].trim() === '') {
|
|
260
|
+
missing.push(field);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Check for empty optional fields
|
|
265
|
+
for (const field of schema.optional || []) {
|
|
266
|
+
if (config[field] === '') {
|
|
267
|
+
warnings.push(`${field} is empty, using default`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
valid: missing.length === 0,
|
|
273
|
+
missing,
|
|
274
|
+
warnings
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Merge multiple configurations
|
|
280
|
+
* @param {Object} base - Base configuration
|
|
281
|
+
* @param {...Object} overrides - Override configurations
|
|
282
|
+
* @returns {Object} Merged configuration
|
|
283
|
+
*/
|
|
284
|
+
mergeConfigs(base, ...overrides) {
|
|
285
|
+
const result = { ...base };
|
|
286
|
+
|
|
287
|
+
for (const override of overrides) {
|
|
288
|
+
for (const [key, value] of Object.entries(override)) {
|
|
289
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
290
|
+
result[key] = value;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return result;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Check if a specific configuration is complete
|
|
300
|
+
* @param {string} schemaName - Schema name to check
|
|
301
|
+
* @returns {Promise<{complete: boolean, missing: string[]}>}
|
|
302
|
+
*/
|
|
303
|
+
async isConfigured(schemaName) {
|
|
304
|
+
const config = await this.loadAll();
|
|
305
|
+
const validation = this.validateConfig(config, schemaName);
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
complete: validation.valid,
|
|
309
|
+
missing: validation.missing
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Get configuration for a specific workflow
|
|
315
|
+
* @param {string} workflowName - Workflow name
|
|
316
|
+
* @returns {Promise<Object>}
|
|
317
|
+
*/
|
|
318
|
+
async getWorkflowConfig(workflowName) {
|
|
319
|
+
const config = await this.loadAll();
|
|
320
|
+
const prefix = workflowName.toUpperCase() + '_';
|
|
321
|
+
|
|
322
|
+
const workflowConfig = {};
|
|
323
|
+
for (const [key, value] of Object.entries(config)) {
|
|
324
|
+
if (key.startsWith(prefix) || key.startsWith(workflowName.toUpperCase())) {
|
|
325
|
+
workflowConfig[key] = value;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return workflowConfig;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Parse .env content into object
|
|
334
|
+
* @param {string} content - .env file content
|
|
335
|
+
* @returns {Object}
|
|
336
|
+
*/
|
|
337
|
+
parseEnvContent(content) {
|
|
338
|
+
const result = {};
|
|
339
|
+
const lines = content.split('\n');
|
|
340
|
+
|
|
341
|
+
for (const line of lines) {
|
|
342
|
+
const trimmed = line.trim();
|
|
343
|
+
|
|
344
|
+
// Skip empty lines and comments
|
|
345
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const match = trimmed.match(/^([^=]+)=(.*)$/);
|
|
350
|
+
if (match) {
|
|
351
|
+
const key = match[1].trim();
|
|
352
|
+
let value = match[2].trim();
|
|
353
|
+
|
|
354
|
+
// Remove quotes if present
|
|
355
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
356
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
357
|
+
value = value.slice(1, -1);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
result[key] = value;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Format object as .env content
|
|
369
|
+
* @param {Object} config - Configuration object
|
|
370
|
+
* @returns {string}
|
|
371
|
+
*/
|
|
372
|
+
formatEnvContent(config) {
|
|
373
|
+
const lines = [];
|
|
374
|
+
|
|
375
|
+
// Group by prefix for better organization
|
|
376
|
+
const groups = {};
|
|
377
|
+
const noPrefix = [];
|
|
378
|
+
|
|
379
|
+
for (const [key, value] of Object.entries(config)) {
|
|
380
|
+
if (value === undefined || value === null) continue;
|
|
381
|
+
|
|
382
|
+
const prefixMatch = key.match(/^([A-Z]+)_/);
|
|
383
|
+
if (prefixMatch) {
|
|
384
|
+
const prefix = prefixMatch[1];
|
|
385
|
+
if (!groups[prefix]) groups[prefix] = [];
|
|
386
|
+
groups[prefix].push([key, value]);
|
|
387
|
+
} else {
|
|
388
|
+
noPrefix.push([key, value]);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Format groups
|
|
393
|
+
for (const [prefix, entries] of Object.entries(groups)) {
|
|
394
|
+
lines.push(`# ${prefix} Configuration`);
|
|
395
|
+
for (const [key, value] of entries) {
|
|
396
|
+
lines.push(this.formatEnvLine(key, value));
|
|
397
|
+
}
|
|
398
|
+
lines.push('');
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Format ungrouped
|
|
402
|
+
if (noPrefix.length > 0) {
|
|
403
|
+
lines.push('# General Configuration');
|
|
404
|
+
for (const [key, value] of noPrefix) {
|
|
405
|
+
lines.push(this.formatEnvLine(key, value));
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return lines.join('\n');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Format a single .env line
|
|
414
|
+
* @param {string} key - Key
|
|
415
|
+
* @param {any} value - Value
|
|
416
|
+
* @returns {string}
|
|
417
|
+
*/
|
|
418
|
+
formatEnvLine(key, value) {
|
|
419
|
+
const stringValue = String(value);
|
|
420
|
+
const needsQuotes = stringValue.includes(' ') ||
|
|
421
|
+
stringValue.includes('#') ||
|
|
422
|
+
stringValue.includes('=');
|
|
423
|
+
|
|
424
|
+
return needsQuotes ? `${key}="${stringValue}"` : `${key}=${stringValue}`;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Delete a configuration key
|
|
429
|
+
* @param {string} key - Key to delete
|
|
430
|
+
* @returns {Promise<boolean>}
|
|
431
|
+
*/
|
|
432
|
+
async deleteConfig(key) {
|
|
433
|
+
const config = await this.loadEnvFile();
|
|
434
|
+
|
|
435
|
+
if (config[key] !== undefined) {
|
|
436
|
+
delete config[key];
|
|
437
|
+
await this.saveEnvConfig(config, false);
|
|
438
|
+
this.configCache = null;
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* List all configuration keys
|
|
447
|
+
* @returns {Promise<string[]>}
|
|
448
|
+
*/
|
|
449
|
+
async listConfigKeys() {
|
|
450
|
+
const config = await this.loadAll();
|
|
451
|
+
return Object.keys(config).sort();
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Singleton instance for convenience
|
|
456
|
+
let defaultInstance = null;
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Get the default ConfigManager instance
|
|
460
|
+
* @param {string} [projectRoot] - Project root directory
|
|
461
|
+
* @returns {ConfigManager}
|
|
462
|
+
*/
|
|
463
|
+
export function getConfigManager(projectRoot) {
|
|
464
|
+
if (!defaultInstance || (projectRoot && projectRoot !== defaultInstance.projectRoot)) {
|
|
465
|
+
defaultInstance = new ConfigManager(projectRoot);
|
|
466
|
+
}
|
|
467
|
+
return defaultInstance;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export default ConfigManager;
|