@burgan-tech/vnext-workflow-cli 1.0.1 → 1.0.2

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.
@@ -3,200 +3,318 @@ const ora = require('ora');
3
3
  const path = require('path');
4
4
  const inquirer = require('inquirer');
5
5
  const config = require('../lib/config');
6
- const { discoverComponents } = require('../lib/discover');
7
- const { reinitializeSystem } = require('../lib/api');
8
- const {
9
- processWorkflow,
10
- getGitChangedJson,
11
- findAllJson
12
- } = require('../lib/workflow');
13
- const {
14
- processCsxFile,
15
- getGitChangedCsx,
16
- findAllCsx
17
- } = require('../lib/csx');
6
+ const { discoverComponents, findAllJsonFiles } = require('../lib/discover');
7
+ const { getDomain, getComponentTypes } = require('../lib/vnextConfig');
8
+ const { publishComponent, reinitializeSystem } = require('../lib/api');
9
+ const { getInstanceId, deleteWorkflow } = require('../lib/db');
10
+ const { getJsonMetadata, getGitChangedJson, findAllJson, detectComponentType } = require('../lib/workflow');
11
+ const { processCsxFile, getGitChangedCsx, findAllCsx } = require('../lib/csx');
12
+
13
+ // Logging helpers
14
+ const LOG = {
15
+ separator: () => console.log(chalk.cyan('═'.repeat(60))),
16
+ subSeparator: () => console.log(chalk.cyan('─'.repeat(60))),
17
+ header: (text) => {
18
+ console.log();
19
+ LOG.separator();
20
+ console.log(chalk.cyan.bold(` ${text}`));
21
+ LOG.separator();
22
+ },
23
+ success: (text) => console.log(chalk.green(` ✓ ${text}`)),
24
+ error: (text) => console.log(chalk.red(` ✗ ${text}`)),
25
+ warning: (text) => console.log(chalk.yellow(` ⚠ ${text}`)),
26
+ info: (text) => console.log(chalk.dim(` ○ ${text}`)),
27
+ component: (type, name, status, detail = '') => {
28
+ const typeLabel = chalk.cyan(`[${type}]`);
29
+ const nameLabel = chalk.white(name);
30
+ if (status === 'success') {
31
+ console.log(` ${typeLabel} ${chalk.green('✓')} ${nameLabel} ${chalk.dim(detail)}`);
32
+ } else if (status === 'error') {
33
+ console.log(` ${typeLabel} ${chalk.red('✗')} ${nameLabel}`);
34
+ if (detail) console.log(chalk.red(` └─ ${detail}`));
35
+ } else if (status === 'skip') {
36
+ console.log(` ${typeLabel} ${chalk.dim('○')} ${nameLabel} ${chalk.dim(detail)}`);
37
+ }
38
+ }
39
+ };
18
40
 
19
41
  async function updateCommand(options) {
20
- console.log(chalk.cyan.bold('\n🔄 Workflow Güncelleme\n'));
42
+ LOG.header('COMPONENT UPDATE');
21
43
 
22
44
  const projectRoot = config.get('PROJECT_ROOT');
23
45
  const autoDiscover = config.get('AUTO_DISCOVER');
24
46
 
47
+ // Get domain from vnext.config.json
48
+ let domain;
49
+ try {
50
+ domain = getDomain(projectRoot);
51
+ } catch (error) {
52
+ LOG.error(`Failed to read vnext.config.json: ${error.message}`);
53
+ return;
54
+ }
55
+
25
56
  // DB Config
57
+ const useDockerValue = config.get('USE_DOCKER');
26
58
  const dbConfig = {
27
59
  host: config.get('DB_HOST'),
28
60
  port: config.get('DB_PORT'),
29
61
  database: config.get('DB_NAME'),
30
62
  user: config.get('DB_USER'),
31
63
  password: config.get('DB_PASSWORD'),
32
- useDocker: config.get('USE_DOCKER'),
64
+ useDocker: useDockerValue === true || useDockerValue === 'true',
33
65
  dockerContainer: config.get('DOCKER_POSTGRES_CONTAINER')
34
66
  };
35
67
 
36
68
  // API Config
37
69
  const apiConfig = {
38
70
  baseUrl: config.get('API_BASE_URL'),
39
- version: config.get('API_VERSION')
71
+ version: config.get('API_VERSION'),
72
+ domain: domain
40
73
  };
41
74
 
42
- // ÖNCELİKLE: Değişen CSX dosyalarını güncelle
75
+ console.log(chalk.dim(` Domain: ${domain}`));
76
+ console.log(chalk.dim(` API: ${apiConfig.baseUrl}`));
77
+ console.log();
78
+
79
+ // FIRST: Update changed CSX files
43
80
  let csxFiles = [];
81
+ const csxResults = { success: 0, failed: 0, errors: [] };
44
82
 
45
83
  if (options.all) {
46
- // Tüm CSX'leri bul
47
- const csxSpinner = ora('Tüm CSX dosyaları bulunuyor...').start();
48
- csxFiles = await findAllCsx(projectRoot);
49
- csxSpinner.succeed(chalk.green(`${csxFiles.length} CSX dosyası bulundu`));
84
+ // Find all CSX files
85
+ const csxSpinner = ora('Finding all CSX files...').start();
86
+ try {
87
+ csxFiles = await findAllCsx(projectRoot);
88
+ csxSpinner.succeed(chalk.green(`${csxFiles.length} CSX files found`));
89
+ } catch (error) {
90
+ csxSpinner.warn(chalk.yellow(`CSX scan error: ${error.message}`));
91
+ }
50
92
  } else {
51
- // Git'te değişen CSX'leri bul
52
- const csxSpinner = ora('Git\'te değişen CSX dosyaları aranıyor...').start();
53
- csxFiles = await getGitChangedCsx(projectRoot);
54
-
55
- if (csxFiles.length > 0) {
56
- csxSpinner.succeed(chalk.green(`${csxFiles.length} değişen CSX dosyası bulundu`));
57
- } else {
58
- csxSpinner.info(chalk.dim('Değişen CSX dosyası yok'));
93
+ // Find changed CSX files in Git
94
+ const csxSpinner = ora('Finding changed CSX files in Git...').start();
95
+ try {
96
+ csxFiles = await getGitChangedCsx(projectRoot);
97
+
98
+ if (csxFiles.length > 0) {
99
+ csxSpinner.succeed(chalk.green(`${csxFiles.length} changed CSX files found`));
100
+ } else {
101
+ csxSpinner.info(chalk.dim('No changed CSX files'));
102
+ }
103
+ } catch (error) {
104
+ csxSpinner.warn(chalk.yellow(`CSX scan error: ${error.message}`));
59
105
  }
60
106
  }
61
107
 
62
- // CSX dosyalarını güncelle
108
+ // Update CSX files
63
109
  if (csxFiles.length > 0) {
64
- console.log(chalk.blue('\n📝 CSX dosyaları JSON\'lara yazılıyor...\n'));
110
+ console.log(chalk.blue('\n Writing CSX files to JSONs...\n'));
65
111
 
66
- let csxSuccessCount = 0;
67
112
  for (const csxFile of csxFiles) {
68
113
  const fileName = path.basename(csxFile);
69
- const csxSpinner = ora(`Base64 encode: ${fileName}`).start();
70
114
 
71
115
  try {
72
116
  const result = await processCsxFile(csxFile, projectRoot);
73
117
 
74
118
  if (result.success) {
75
- csxSpinner.succeed(chalk.green(`✓ ${fileName} ${result.updatedCount} JSON`));
76
- csxSuccessCount++;
119
+ LOG.component('CSX', fileName, 'success', `→ ${result.updatedJsonCount} JSON, ${result.totalUpdates} refs`);
120
+ csxResults.success++;
77
121
  } else {
78
- csxSpinner.warn(chalk.yellow(`○ ${fileName} ${result.message}`));
122
+ LOG.component('CSX', fileName, 'skip', result.message);
79
123
  }
80
124
  } catch (error) {
81
- csxSpinner.fail(chalk.red(`✗ ${fileName} ${error.message}`));
125
+ LOG.component('CSX', fileName, 'error', error.message);
126
+ csxResults.failed++;
127
+ csxResults.errors.push({ file: fileName, error: error.message });
82
128
  }
83
129
  }
84
-
85
- if (csxSuccessCount > 0) {
86
- console.log(chalk.green(`\n✓ ${csxSuccessCount} CSX dosyası güncellendi\n`));
87
- }
88
130
  }
89
131
 
90
132
  let jsonFiles = [];
91
133
 
92
- // Hangi JSON dosyalarını işleyeceğiz?
134
+ // Which JSON files to process?
93
135
  if (options.file) {
94
- // Belirli dosya
136
+ // Specific file
95
137
  const filePath = path.isAbsolute(options.file)
96
138
  ? options.file
97
139
  : path.join(projectRoot, options.file);
98
- jsonFiles = [filePath];
99
- console.log(chalk.blue(`Dosya: ${path.basename(filePath)}\n`));
140
+ jsonFiles = [{ path: filePath, type: detectComponentType(filePath, projectRoot), fileName: path.basename(filePath) }];
141
+ console.log(chalk.blue(`\n File: ${path.basename(filePath)}\n`));
100
142
  } else if (options.all) {
101
- // Tüm JSON dosyaları
102
- console.log(chalk.yellow('⚠️ TÜM workflow\'lar güncellenecek!\n'));
143
+ // All JSON files
144
+ LOG.warning('ALL components will be updated!');
145
+ console.log();
103
146
 
104
147
  const { confirm } = await inquirer.prompt([{
105
148
  type: 'confirm',
106
149
  name: 'confirm',
107
- message: 'Devam edilsin mi?',
150
+ message: 'Continue?',
108
151
  default: false
109
152
  }]);
110
153
 
111
154
  if (!confirm) {
112
- console.log(chalk.yellow('\nİşlem iptal edildi.\n'));
155
+ LOG.warning('Operation cancelled.');
156
+ console.log();
113
157
  return;
114
158
  }
115
159
 
116
- const spinner = ora('Tüm JSON dosyaları bulunuyor...').start();
160
+ const spinner = ora('Finding all JSON files...').start();
117
161
 
118
162
  if (autoDiscover) {
119
163
  const discovered = await discoverComponents(projectRoot);
120
- jsonFiles = await findAllJson(discovered);
121
- } else {
122
- // Fallback
123
- jsonFiles = [];
164
+ const files = await findAllJson(discovered);
165
+ jsonFiles = files.map(f => ({
166
+ path: f,
167
+ type: detectComponentType(f, projectRoot),
168
+ fileName: path.basename(f)
169
+ }));
124
170
  }
125
171
 
126
- spinner.succeed(chalk.green(`${jsonFiles.length} JSON dosyası bulundu`));
172
+ spinner.succeed(chalk.green(`${jsonFiles.length} JSON files found`));
127
173
  } else {
128
- // Git'te değişenler (default)
129
- const spinner = ora('Git\'te değişen JSON dosyaları aranıyor...').start();
130
- jsonFiles = await getGitChangedJson(projectRoot);
174
+ // Changed files in Git (default)
175
+ const spinner = ora('Finding changed JSON files in Git...').start();
176
+ const changedFiles = await getGitChangedJson(projectRoot);
131
177
 
132
- if (jsonFiles.length === 0) {
133
- spinner.info(chalk.yellow('Git\'te değişen JSON dosyası bulunamadı'));
134
- console.log(chalk.green('\n✓ Tüm workflow\'lar güncel\n'));
178
+ if (changedFiles.length === 0) {
179
+ spinner.info(chalk.yellow('No changed JSON files in Git'));
180
+ console.log(chalk.green('\n All components up to date\n'));
135
181
  return;
136
182
  }
137
183
 
138
- spinner.succeed(chalk.green(`${jsonFiles.length} değişen JSON dosyası bulundu`));
184
+ jsonFiles = changedFiles.map(f => ({
185
+ path: f,
186
+ type: detectComponentType(f, projectRoot),
187
+ fileName: path.basename(f)
188
+ }));
189
+
190
+ spinner.succeed(chalk.green(`${jsonFiles.length} changed JSON files found`));
139
191
  }
140
192
 
141
- // Her JSON dosyasını işle
142
- let successCount = 0;
143
- let failCount = 0;
193
+ // Group by component type
194
+ const componentStats = {};
195
+ const errors = [];
144
196
 
145
- console.log();
146
- for (const jsonFile of jsonFiles) {
147
- const fileName = path.basename(jsonFile);
148
- const spinner = ora(`İşleniyor: ${fileName}`).start();
197
+ console.log(chalk.blue('\n Publishing components...\n'));
198
+
199
+ for (const jsonInfo of jsonFiles) {
200
+ const { path: jsonPath, type, fileName } = jsonInfo;
201
+
202
+ // Initialize stats
203
+ if (!componentStats[type]) {
204
+ componentStats[type] = { success: 0, failed: 0, skipped: 0, updated: 0, created: 0 };
205
+ }
149
206
 
150
207
  try {
151
- const result = await processWorkflow(jsonFile, dbConfig, apiConfig);
208
+ const metadata = await getJsonMetadata(jsonPath);
152
209
 
153
- const status = result.wasDeleted ? 'güncellendi' : 'oluşturuldu';
154
- spinner.succeed(chalk.green(`✓ ${fileName} ${status}`));
155
- successCount++;
156
- } catch (error) {
157
- let errorMsg = error.message;
158
- if (error.response?.data) {
159
- if (typeof error.response.data === 'string') {
160
- errorMsg = error.response.data;
161
- } else if (error.response.data.error?.message) {
162
- errorMsg = error.response.data.error.message;
163
- } else if (error.response.data.message) {
164
- errorMsg = error.response.data.message;
210
+ if (!metadata.key || !metadata.version) {
211
+ LOG.component(type, fileName, 'skip', 'no key/version');
212
+ componentStats[type].skipped++;
213
+ continue;
214
+ }
215
+
216
+ // Detect flow type
217
+ const flow = metadata.flow || detectComponentType(jsonPath, projectRoot);
218
+
219
+ // Check if exists in DB
220
+ const existingId = await getInstanceId(dbConfig, flow, metadata.key, metadata.version);
221
+
222
+ // If exists, delete first
223
+ let wasDeleted = false;
224
+ if (existingId) {
225
+ await deleteWorkflow(dbConfig, flow, existingId);
226
+ wasDeleted = true;
227
+ }
228
+
229
+ // Publish to API
230
+ const result = await publishComponent(apiConfig.baseUrl, metadata.data);
231
+
232
+ if (result.success) {
233
+ if (wasDeleted) {
234
+ LOG.component(type, fileName, 'success', '→ updated');
235
+ componentStats[type].updated++;
165
236
  } else {
166
- errorMsg = JSON.stringify(error.response.data);
237
+ LOG.component(type, fileName, 'success', '→ created');
238
+ componentStats[type].created++;
167
239
  }
240
+ componentStats[type].success++;
241
+ } else {
242
+ LOG.component(type, fileName, 'error', result.error);
243
+ componentStats[type].failed++;
244
+ errors.push({ type, file: fileName, error: result.error });
168
245
  }
169
- spinner.fail(chalk.red(`✗ ${fileName} ${errorMsg}`));
170
- failCount++;
246
+ } catch (error) {
247
+ const errorMsg = error.message || 'Unknown error';
248
+ LOG.component(type, fileName, 'error', errorMsg);
249
+ componentStats[type].failed++;
250
+ errors.push({ type, file: fileName, error: errorMsg });
171
251
  }
172
252
  }
173
253
 
174
254
  // Re-initialize
175
- if (successCount > 0) {
255
+ const totalSuccess = Object.values(componentStats).reduce((sum, s) => sum + s.success, 0);
256
+
257
+ if (totalSuccess > 0) {
176
258
  console.log();
177
- const reinitSpinner = ora('Sistem yeniden başlatılıyor...').start();
259
+ const reinitSpinner = ora('Re-initializing system...').start();
178
260
  const reinitSuccess = await reinitializeSystem(apiConfig.baseUrl, apiConfig.version);
179
261
 
180
262
  if (reinitSuccess) {
181
- reinitSpinner.succeed(chalk.green(' Sistem yenilendi'));
263
+ reinitSpinner.succeed(chalk.green('System re-initialized'));
182
264
  } else {
183
- reinitSpinner.warn(chalk.yellow(' Sistem yenilenemedi (devam edildi)'));
265
+ reinitSpinner.warn(chalk.yellow('System re-initialization failed (continuing)'));
184
266
  }
185
267
  }
186
268
 
187
- // Özet
188
- console.log();
189
- console.log(chalk.cyan('═'.repeat(50)));
190
- console.log(chalk.white(`Toplam: ${jsonFiles.length} dosya`));
191
- console.log(chalk.green(`✓ Başarılı: ${successCount}`));
192
- if (failCount > 0) {
193
- console.log(chalk.red(`✗ Başarısız: ${failCount}`));
269
+ // SUMMARY REPORT
270
+ LOG.header('UPDATE SUMMARY');
271
+
272
+ // Component statistics
273
+ console.log(chalk.white.bold('\n Component Update Results:\n'));
274
+
275
+ for (const [type, stats] of Object.entries(componentStats)) {
276
+ const updatedLabel = stats.updated > 0 ? chalk.green(`${stats.updated} updated`) : '';
277
+ const createdLabel = stats.created > 0 ? chalk.green(`${stats.created} created`) : '';
278
+ const failedLabel = stats.failed > 0 ? chalk.red(`${stats.failed} failed`) : '';
279
+ const skippedLabel = stats.skipped > 0 ? chalk.dim(`${stats.skipped} skipped`) : '';
280
+
281
+ const parts = [updatedLabel, createdLabel, failedLabel, skippedLabel].filter(Boolean);
282
+ console.log(` ${chalk.cyan(type.padEnd(12))} : ${parts.join(', ') || chalk.dim('0')}`);
194
283
  }
195
- console.log(chalk.cyan('═'.repeat(50)));
196
- console.log();
197
284
 
198
- if (successCount > 0) {
199
- console.log(chalk.green.bold('✓ Workflow güncelleme tamamlandı\n'));
285
+ // CSX summary
286
+ if (csxFiles.length > 0) {
287
+ console.log();
288
+ const csxSuccessLabel = csxResults.success > 0 ? chalk.green(`${csxResults.success} success`) : chalk.dim('0 success');
289
+ const csxFailedLabel = csxResults.failed > 0 ? chalk.red(`, ${csxResults.failed} failed`) : '';
290
+ console.log(` ${chalk.cyan('CSX'.padEnd(12))} : ${csxSuccessLabel}${csxFailedLabel}`);
291
+ }
292
+
293
+ // Errors
294
+ if (errors.length > 0 || csxResults.errors.length > 0) {
295
+ console.log();
296
+ LOG.subSeparator();
297
+ console.log(chalk.red.bold('\n ERRORS:\n'));
298
+
299
+ for (const err of errors) {
300
+ console.log(chalk.red(` [${err.type}] ${err.file}`));
301
+ console.log(chalk.dim(` └─ ${err.error}`));
302
+ }
303
+
304
+ for (const err of csxResults.errors) {
305
+ console.log(chalk.red(` [CSX] ${err.file}`));
306
+ console.log(chalk.dim(` └─ ${err.error}`));
307
+ }
308
+ }
309
+
310
+ LOG.separator();
311
+
312
+ const totalFailed = Object.values(componentStats).reduce((sum, s) => sum + s.failed, 0) + csxResults.failed;
313
+
314
+ if (totalSuccess > 0 && totalFailed === 0) {
315
+ console.log(chalk.green.bold('\n ✓ Update completed\n'));
316
+ } else if (totalFailed > 0) {
317
+ console.log(chalk.yellow.bold(`\n ⚠ Update completed (${totalFailed} errors)\n`));
200
318
  }
201
319
  }
202
320
 
package/src/lib/api.js CHANGED
@@ -2,6 +2,8 @@ const axios = require('axios');
2
2
 
3
3
  /**
4
4
  * API bağlantısını test eder
5
+ * @param {string} baseUrl - API base URL
6
+ * @returns {Promise<boolean>} Bağlantı durumu
5
7
  */
6
8
  async function testApiConnection(baseUrl) {
7
9
  try {
@@ -15,46 +17,64 @@ async function testApiConnection(baseUrl) {
15
17
  }
16
18
 
17
19
  /**
18
- * Workflow'u API'ye post eder
20
+ * Komponenti API'ye publish eder
21
+ * @param {string} baseUrl - API base URL
22
+ * @param {Object} componentData - Komponent JSON verisi
23
+ * @returns {Promise<Object>} API response
19
24
  */
20
- async function postWorkflow(baseUrl, version, flow, data) {
21
- const domain = 'core'; // API_DOMAIN
22
- const workflowVersion = data.version || '1.0.0';
23
- const syncMode = 'true';
25
+ async function publishComponent(baseUrl, componentData) {
26
+ const url = `${baseUrl}/api/v1/definitions/publish`;
24
27
 
25
- const url = `${baseUrl}/api/${version}/${domain}/workflows/${flow}/instances/start?version=${workflowVersion}&sync=${syncMode}`;
26
-
27
- const response = await axios.post(url, data, {
28
- headers: {
29
- 'accept': '*/*',
30
- 'Content-Type': 'application/json'
31
- }
32
- });
33
- return response.data;
34
- }
35
-
36
- /**
37
- * Workflow'u aktif eder
38
- */
39
- async function activateWorkflow(baseUrl, version, flow, instanceId, workflowVersion) {
40
- const domain = 'core'; // API_DOMAIN
41
- const syncMode = 'true';
42
-
43
- const url = `${baseUrl}/api/${version}/${domain}/workflows/${flow}/instances/${instanceId}/transitions/activate?version=${workflowVersion}&sync=${syncMode}`;
44
-
45
- const response = await axios.patch(url, null, {
46
- headers: {
47
- 'accept': '*/*'
28
+ try {
29
+ const response = await axios.post(url, componentData, {
30
+ headers: {
31
+ 'accept': '*/*',
32
+ 'Content-Type': 'application/json'
33
+ },
34
+ timeout: 30000
35
+ });
36
+
37
+ return {
38
+ success: true,
39
+ data: response.data
40
+ };
41
+ } catch (error) {
42
+ // API hata detaylarını çıkar
43
+ let errorMessage = error.message;
44
+ let errorDetails = null;
45
+
46
+ if (error.response) {
47
+ const responseData = error.response.data;
48
+
49
+ if (typeof responseData === 'string') {
50
+ errorMessage = responseData;
51
+ } else if (responseData?.error?.message) {
52
+ errorMessage = responseData.error.message;
53
+ errorDetails = responseData.error;
54
+ } else if (responseData?.message) {
55
+ errorMessage = responseData.message;
56
+ } else if (responseData) {
57
+ errorMessage = JSON.stringify(responseData);
58
+ }
48
59
  }
49
- });
50
- return response.data;
60
+
61
+ return {
62
+ success: false,
63
+ error: errorMessage,
64
+ errorDetails: errorDetails,
65
+ statusCode: error.response?.status
66
+ };
67
+ }
51
68
  }
52
69
 
53
70
  /**
54
71
  * Sistemi yeniden başlatır
72
+ * @param {string} baseUrl - API base URL
73
+ * @param {string} version - API version
74
+ * @returns {Promise<boolean>} Başarı durumu
55
75
  */
56
76
  async function reinitializeSystem(baseUrl, version) {
57
- const url = `${baseUrl}/api/${version}/admin/re-initialize`;
77
+ const url = `${baseUrl}/api/${version}/definitions/re-initialize`;
58
78
  try {
59
79
  await axios.get(url, { timeout: 10000 });
60
80
  return true;
@@ -65,8 +85,6 @@ async function reinitializeSystem(baseUrl, version) {
65
85
 
66
86
  module.exports = {
67
87
  testApiConnection,
68
- postWorkflow,
69
- activateWorkflow,
88
+ publishComponent,
70
89
  reinitializeSystem
71
90
  };
72
-
package/src/lib/config.js CHANGED
@@ -4,7 +4,6 @@ const path = require('path');
4
4
  const config = new Conf({
5
5
  projectName: 'vnext-workflow-cli',
6
6
  defaults: {
7
- PROJECT_ROOT: process.cwd(),
8
7
  AUTO_DISCOVER: true,
9
8
  API_BASE_URL: 'http://localhost:4201',
10
9
  API_VERSION: 'v1',
@@ -19,11 +18,50 @@ const config = new Conf({
19
18
  }
20
19
  });
21
20
 
21
+ /**
22
+ * Gets a config value
23
+ * PROJECT_ROOT always returns current working directory (process.cwd())
24
+ * @param {string} key - Config key
25
+ * @returns {any} Config value
26
+ */
27
+ function get(key) {
28
+ // PROJECT_ROOT is always the current working directory
29
+ if (key === 'PROJECT_ROOT') {
30
+ return process.cwd();
31
+ }
32
+ return config.get(key);
33
+ }
34
+
35
+ /**
36
+ * Sets a config value
37
+ * PROJECT_ROOT cannot be set (ignored)
38
+ * @param {string} key - Config key
39
+ * @param {any} value - Config value
40
+ */
41
+ function set(key, value) {
42
+ // PROJECT_ROOT cannot be saved - always uses cwd
43
+ if (key === 'PROJECT_ROOT') {
44
+ console.log('Note: PROJECT_ROOT is always the current working directory and cannot be changed.');
45
+ return;
46
+ }
47
+ config.set(key, value);
48
+ }
49
+
50
+ /**
51
+ * Gets all config values including PROJECT_ROOT
52
+ * @returns {Object} All config values
53
+ */
54
+ function getAll() {
55
+ return {
56
+ PROJECT_ROOT: process.cwd(),
57
+ ...config.store
58
+ };
59
+ }
60
+
22
61
  module.exports = {
23
- get: (key) => config.get(key),
24
- set: (key, value) => config.set(key, value),
25
- getAll: () => config.store,
62
+ get,
63
+ set,
64
+ getAll,
26
65
  clear: () => config.clear(),
27
66
  path: config.path
28
67
  };
29
-