@comfanion/workflow 4.36.3 → 4.36.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.
package/bin/cli.js CHANGED
@@ -8,6 +8,24 @@ import fs from 'fs-extra';
8
8
  import path from 'path';
9
9
  import { fileURLToPath } from 'url';
10
10
  import { execSync } from 'child_process';
11
+ import yaml from 'js-yaml';
12
+
13
+ /**
14
+ * Deep merge two objects. User values override defaults.
15
+ * Arrays are replaced, not merged.
16
+ */
17
+ function deepMerge(defaults, user) {
18
+ const result = { ...defaults };
19
+ for (const key of Object.keys(user)) {
20
+ if (user[key] !== null && typeof user[key] === 'object' && !Array.isArray(user[key]) &&
21
+ defaults[key] !== null && typeof defaults[key] === 'object' && !Array.isArray(defaults[key])) {
22
+ result[key] = deepMerge(defaults[key], user[key]);
23
+ } else {
24
+ result[key] = user[key];
25
+ }
26
+ }
27
+ return result;
28
+ }
11
29
 
12
30
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
31
  const PACKAGE_DIR = path.join(__dirname, '..');
@@ -572,59 +590,35 @@ program
572
590
  await fs.move(tempVectors, path.join(targetDir, 'vectors'), { overwrite: true });
573
591
  }
574
592
 
575
- // Restore user's config.yaml with new sections if missing
576
- spinner.text = 'Restoring config.yaml...';
577
- let mergedConfig = configBackup;
578
-
579
- // Extract user's custom exclude patterns before merge
580
- const userExcludeMatch = configBackup.match(/exclude:\s*\n((?:\s+-\s+.+\n?)+)/);
581
- const userExcludes = userExcludeMatch
582
- ? userExcludeMatch[1].match(/-\s+(.+)/g)?.map(m => m.replace(/^-\s+/, '').trim()) || []
583
- : [];
584
-
585
- // Add vectorizer section if missing
586
- if (!mergedConfig.includes('vectorizer:')) {
587
- const newConfigPath = path.join(targetDir, 'config.yaml');
588
- const newConfig = await fs.readFile(newConfigPath, 'utf8');
589
- const vectorizerMatch = newConfig.match(/(# =+\n# VECTORIZER[\s\S]*?)(?=# =+\n# [A-Z])/);
590
- if (vectorizerMatch) {
591
- // Insert before LSP section or at end
592
- const insertPoint = mergedConfig.indexOf('# =============================================================================\n# LSP');
593
- if (insertPoint > 0) {
594
- mergedConfig = mergedConfig.slice(0, insertPoint) + vectorizerMatch[1] + mergedConfig.slice(insertPoint);
595
- } else {
596
- mergedConfig += '\n' + vectorizerMatch[1];
597
- }
598
- console.log(chalk.green(' ✅ Added new vectorizer configuration section'));
599
- }
600
- }
601
-
602
- // Merge user's custom excludes into vectorizer section
603
- if (userExcludes.length > 0) {
593
+ // Merge config.yaml: new defaults + user overrides
594
+ spinner.text = 'Merging config.yaml...';
595
+ try {
604
596
  const newConfigPath = path.join(targetDir, 'config.yaml');
605
- const newConfig = await fs.readFile(newConfigPath, 'utf8');
606
- const defaultExcludeMatch = newConfig.match(/exclude:\s*\n((?:\s+-\s+.+\n?)+)/);
607
- const defaultExcludes = defaultExcludeMatch
608
- ? defaultExcludeMatch[1].match(/-\s+(.+)/g)?.map(m => m.replace(/^-\s+/, '').trim()) || []
609
- : [];
597
+ const newConfigContent = await fs.readFile(newConfigPath, 'utf8');
610
598
 
611
- // Find excludes that user added (not in defaults)
612
- const customExcludes = userExcludes.filter(e => !defaultExcludes.includes(e));
599
+ // Parse both configs
600
+ const newConfig = yaml.load(newConfigContent) || {};
601
+ const userConfig = yaml.load(configBackup) || {};
613
602
 
614
- if (customExcludes.length > 0) {
615
- // Add custom excludes to the merged config
616
- const excludeInsertPoint = mergedConfig.match(/exclude:\s*\n((?:\s+-\s+.+\n?)+)/);
617
- if (excludeInsertPoint) {
618
- const insertAfter = excludeInsertPoint.index + excludeInsertPoint[0].length;
619
- const customLines = customExcludes.map(e => ` - ${e}`).join('\n') + '\n';
620
- mergedConfig = mergedConfig.slice(0, insertAfter) + customLines + mergedConfig.slice(insertAfter);
621
- console.log(chalk.green(` ✅ Preserved ${customExcludes.length} custom exclude patterns`));
622
- }
623
- }
603
+ // Deep merge: defaults from new config, overridden by user values
604
+ const mergedConfig = deepMerge(newConfig, userConfig);
605
+
606
+ // Dump back to YAML with nice formatting
607
+ const mergedContent = yaml.dump(mergedConfig, {
608
+ indent: 2,
609
+ lineWidth: 120,
610
+ noRefs: true,
611
+ sortKeys: false
612
+ });
613
+
614
+ await fs.writeFile(configPath, mergedContent);
615
+ console.log(chalk.green(' ✅ config.yaml merged (your settings preserved, new options added)'));
616
+ } catch (e) {
617
+ // Fallback: just restore user's config if merge fails
618
+ await fs.writeFile(configPath, configBackup);
619
+ console.log(chalk.yellow(' ⚠️ config.yaml restored (merge failed, using your original)'));
624
620
  }
625
621
 
626
- await fs.writeFile(configPath, mergedConfig);
627
-
628
622
  // Install plugin dependencies
629
623
  spinner.text = 'Installing plugin dependencies...';
630
624
  let pluginDepsInstalled = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comfanion/workflow",
3
- "version": "4.36.3",
3
+ "version": "4.36.5",
4
4
  "description": "Initialize OpenCode Workflow system for AI-assisted development with semantic code search",
5
5
  "type": "module",
6
6
  "bin": {
@@ -50,6 +50,7 @@
50
50
  "glob": "^10.5.0",
51
51
  "ignore": "^5.3.0",
52
52
  "inquirer": "^9.2.0",
53
+ "js-yaml": "^4.1.1",
53
54
  "ora": "^7.0.0"
54
55
  }
55
56
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "3.0.0",
3
- "buildDate": "2026-01-24T14:58:40.429Z",
3
+ "buildDate": "2026-01-24T15:01:58.449Z",
4
4
  "files": [
5
5
  "config.yaml",
6
6
  "FLOW.yaml",