@comfanion/workflow 4.36.4 → 4.36.6

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, '..');
@@ -260,6 +278,8 @@ program
260
278
  let hadVectorizer = false;
261
279
  let hadVectors = false;
262
280
 
281
+ let existingConfigContent = null;
282
+
263
283
  if (await fs.pathExists(targetDir)) {
264
284
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
265
285
  const backupDir = path.join(process.cwd(), `.opencode.backup-${timestamp}`);
@@ -268,6 +288,12 @@ program
268
288
  hadVectorizer = await fs.pathExists(vectorizerNodeModules);
269
289
  hadVectors = await fs.pathExists(vectorsDir);
270
290
 
291
+ // Read existing config.yaml for merge
292
+ const existingConfigPath = path.join(targetDir, 'config.yaml');
293
+ if (await fs.pathExists(existingConfigPath)) {
294
+ existingConfigContent = await fs.readFile(existingConfigPath, 'utf8');
295
+ }
296
+
271
297
  // Preserve vectorizer node_modules (100MB+, don't backup)
272
298
  if (hadVectorizer) {
273
299
  spinner.text = 'Preserving vectorizer dependencies...';
@@ -323,18 +349,37 @@ program
323
349
  const configPath = path.join(targetDir, 'config.yaml');
324
350
  let configContent = await fs.readFile(configPath, 'utf8');
325
351
 
352
+ // If we had existing config, merge it (preserve user customizations)
353
+ if (existingConfigContent) {
354
+ try {
355
+ const newConfig = yaml.load(configContent) || {};
356
+ const existingConfig = yaml.load(existingConfigContent) || {};
357
+ const mergedConfig = deepMerge(newConfig, existingConfig);
358
+ configContent = yaml.dump(mergedConfig, {
359
+ indent: 2,
360
+ lineWidth: 120,
361
+ noRefs: true,
362
+ sortKeys: false
363
+ });
364
+ console.log(chalk.green(' ✅ Merged existing config settings'));
365
+ } catch (e) {
366
+ // Merge failed, continue with new config + user values
367
+ }
368
+ }
369
+
370
+ // Apply user's answers from prompts
326
371
  configContent = configContent
327
- .replace(/user_name: ".*"/, `user_name: "${config.user_name}"`)
328
- .replace(/communication_language: ".*"/, `communication_language: "${config.communication_language}"`)
329
- .replace(/project_name: ".*"/, `project_name: "${config.project_name}"`)
372
+ .replace(/user_name: .*/, `user_name: "${config.user_name}"`)
373
+ .replace(/communication_language: .*/, `communication_language: "${config.communication_language}"`)
374
+ .replace(/project_name: .*/, `project_name: "${config.project_name}"`)
330
375
  .replace(/methodology: (tdd|stub)/, `methodology: ${config.methodology}`);
331
376
 
332
377
  // Jira config
333
378
  if (config.jira_enabled) {
334
379
  configContent = configContent
335
380
  .replace(/enabled: false\s+# Jira/, `enabled: true # Jira`)
336
- .replace(/base_url: ".*"/, `base_url: "${config.jira_url}"`)
337
- .replace(/project_key: ".*"/, `project_key: "${config.jira_project}"`);
381
+ .replace(/base_url: .*/, `base_url: "${config.jira_url}"`)
382
+ .replace(/project_key: .*/, `project_key: "${config.jira_project}"`);
338
383
  }
339
384
 
340
385
  // Vectorizer config
@@ -572,10 +617,34 @@ program
572
617
  await fs.move(tempVectors, path.join(targetDir, 'vectors'), { overwrite: true });
573
618
  }
574
619
 
575
- // Restore user's config.yaml completely - only add missing sections
576
- spinner.text = 'Restoring config.yaml...';
577
- await fs.writeFile(configPath, configBackup);
578
- console.log(chalk.green('config.yaml preserved'));
620
+ // Merge config.yaml: new defaults + user overrides
621
+ spinner.text = 'Merging config.yaml...';
622
+ try {
623
+ const newConfigPath = path.join(targetDir, 'config.yaml');
624
+ const newConfigContent = await fs.readFile(newConfigPath, 'utf8');
625
+
626
+ // Parse both configs
627
+ const newConfig = yaml.load(newConfigContent) || {};
628
+ const userConfig = yaml.load(configBackup) || {};
629
+
630
+ // Deep merge: defaults from new config, overridden by user values
631
+ const mergedConfig = deepMerge(newConfig, userConfig);
632
+
633
+ // Dump back to YAML with nice formatting
634
+ const mergedContent = yaml.dump(mergedConfig, {
635
+ indent: 2,
636
+ lineWidth: 120,
637
+ noRefs: true,
638
+ sortKeys: false
639
+ });
640
+
641
+ await fs.writeFile(configPath, mergedContent);
642
+ console.log(chalk.green(' ✅ config.yaml merged (your settings preserved, new options added)'));
643
+ } catch (e) {
644
+ // Fallback: just restore user's config if merge fails
645
+ await fs.writeFile(configPath, configBackup);
646
+ console.log(chalk.yellow(' ⚠️ config.yaml restored (merge failed, using your original)'));
647
+ }
579
648
 
580
649
  // Install plugin dependencies
581
650
  spinner.text = 'Installing plugin dependencies...';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comfanion/workflow",
3
- "version": "4.36.4",
3
+ "version": "4.36.6",
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:59:22.208Z",
3
+ "buildDate": "2026-01-24T15:05:07.173Z",
4
4
  "files": [
5
5
  "config.yaml",
6
6
  "FLOW.yaml",