proagents 1.0.4 → 1.0.5

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.
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync, cpSync, writeFileSync, readFileSync, readdirSync
2
2
  import { join, dirname } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import chalk from 'chalk';
5
+ import yaml from 'js-yaml';
5
6
 
6
7
  const __filename = fileURLToPath(import.meta.url);
7
8
  const __dirname = dirname(__filename);
@@ -9,11 +10,13 @@ const __dirname = dirname(__filename);
9
10
  // Files/folders to preserve during update (user customizations)
10
11
  const PRESERVE_PATHS = [
11
12
  'active-features', // User's work in progress
12
- 'proagents.config.yaml', // User's configuration
13
13
  '.learning', // Learned patterns
14
14
  'cache', // Cached analysis
15
15
  ];
16
16
 
17
+ // Config file is handled specially - merged not preserved
18
+ const CONFIG_FILE = 'proagents.config.yaml';
19
+
17
20
  // Files that should always be updated (framework files)
18
21
  const FRAMEWORK_FOLDERS = [
19
22
  'prompts',
@@ -109,7 +112,7 @@ export async function initCommand(options = {}) {
109
112
  console.log(chalk.green('\n✓ ProAgents updated successfully!\n'));
110
113
  console.log(chalk.gray('Preserved:'));
111
114
  console.log(chalk.gray(' • active-features/ (your work in progress)'));
112
- console.log(chalk.gray(' • proagents.config.yaml (your configuration)'));
115
+ console.log(chalk.gray(' • proagents.config.yaml (your values + new options merged)'));
113
116
  console.log(chalk.gray(' • .learning/ (learned patterns)'));
114
117
  console.log(chalk.gray(' • cache/ (analysis cache)\n'));
115
118
  return;
@@ -314,6 +317,22 @@ async function smartUpdate(sourceDir, targetDir) {
314
317
  }
315
318
  console.log(chalk.green(`✓ Updated ${filesUpdated} framework files`));
316
319
 
320
+ // Merge config file - keep user values, add new options
321
+ const userConfigPath = join(targetDir, CONFIG_FILE);
322
+ const newConfigPath = join(sourceDir, CONFIG_FILE);
323
+
324
+ if (existsSync(newConfigPath)) {
325
+ const mergeResult = mergeConfigs(userConfigPath, newConfigPath);
326
+ if (mergeResult.newOptions > 0) {
327
+ console.log(chalk.green(`✓ Config merged: ${mergeResult.newOptions} new options added`));
328
+ if (mergeResult.newKeys.length > 0 && mergeResult.newKeys.length <= 5) {
329
+ console.log(chalk.gray(` New: ${mergeResult.newKeys.join(', ')}`));
330
+ }
331
+ } else {
332
+ console.log(chalk.green(`✓ Config up to date`));
333
+ }
334
+ }
335
+
317
336
  // Ensure preserved paths still exist (they should, but just in case)
318
337
  for (const [path, fullPath] of Object.entries(backups)) {
319
338
  if (!existsSync(fullPath)) {
@@ -327,3 +346,84 @@ async function smartUpdate(sourceDir, targetDir) {
327
346
  }
328
347
  }
329
348
  }
349
+
350
+ /**
351
+ * Deep merge two objects - source values override target, but target values are preserved
352
+ * Returns the merged object and list of new keys added
353
+ */
354
+ function deepMerge(target, source, path = '') {
355
+ const result = { ...target };
356
+ const newKeys = [];
357
+
358
+ for (const key of Object.keys(source)) {
359
+ const currentPath = path ? `${path}.${key}` : key;
360
+
361
+ if (!(key in target)) {
362
+ // New key - add it
363
+ result[key] = source[key];
364
+ newKeys.push(currentPath);
365
+ } else if (
366
+ typeof source[key] === 'object' &&
367
+ source[key] !== null &&
368
+ !Array.isArray(source[key]) &&
369
+ typeof target[key] === 'object' &&
370
+ target[key] !== null &&
371
+ !Array.isArray(target[key])
372
+ ) {
373
+ // Both are objects - recurse
374
+ const merged = deepMerge(target[key], source[key], currentPath);
375
+ result[key] = merged.result;
376
+ newKeys.push(...merged.newKeys);
377
+ }
378
+ // If key exists in target and is not an object, keep target value
379
+ }
380
+
381
+ return { result, newKeys };
382
+ }
383
+
384
+ /**
385
+ * Merge user config with new framework config
386
+ * Preserves user values, adds new options from framework
387
+ */
388
+ function mergeConfigs(userConfigPath, newConfigPath) {
389
+ try {
390
+ const newConfigContent = readFileSync(newConfigPath, 'utf-8');
391
+ const newConfig = yaml.load(newConfigContent);
392
+
393
+ if (!existsSync(userConfigPath)) {
394
+ // No user config - just copy the new one
395
+ writeFileSync(userConfigPath, newConfigContent);
396
+ return { newOptions: Object.keys(newConfig).length, newKeys: ['(new config created)'] };
397
+ }
398
+
399
+ const userConfigContent = readFileSync(userConfigPath, 'utf-8');
400
+ const userConfig = yaml.load(userConfigContent);
401
+
402
+ if (!userConfig || typeof userConfig !== 'object') {
403
+ // User config is empty or invalid - use new config
404
+ writeFileSync(userConfigPath, newConfigContent);
405
+ return { newOptions: Object.keys(newConfig).length, newKeys: ['(config replaced)'] };
406
+ }
407
+
408
+ // Deep merge - user values take precedence, new keys added
409
+ const { result: mergedConfig, newKeys } = deepMerge(userConfig, newConfig);
410
+
411
+ if (newKeys.length > 0) {
412
+ // Write merged config with comment
413
+ const header = `# ProAgents Configuration\n# User values preserved, new options added during update\n# Last updated: ${new Date().toISOString().split('T')[0]}\n\n`;
414
+ const mergedYaml = yaml.dump(mergedConfig, {
415
+ indent: 2,
416
+ lineWidth: 120,
417
+ noRefs: true,
418
+ sortKeys: false
419
+ });
420
+ writeFileSync(userConfigPath, header + mergedYaml);
421
+ }
422
+
423
+ return { newOptions: newKeys.length, newKeys };
424
+ } catch (error) {
425
+ // If merge fails, don't break the update - just warn
426
+ console.log(chalk.yellow(`⚠️ Could not merge config: ${error.message}`));
427
+ return { newOptions: 0, newKeys: [] };
428
+ }
429
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "proagents",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "AI-agnostic development workflow framework that automates the full software development lifecycle",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -47,6 +47,7 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "commander": "^11.1.0",
50
- "chalk": "^5.3.0"
50
+ "chalk": "^5.3.0",
51
+ "js-yaml": "^4.1.0"
51
52
  }
52
53
  }