@comfanion/workflow 4.36.5 → 4.36.7
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 +66 -41
- package/package.json +1 -1
- package/src/build-info.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -11,20 +11,22 @@ import { execSync } from 'child_process';
|
|
|
11
11
|
import yaml from 'js-yaml';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
* Arrays are replaced, not merged.
|
|
14
|
+
* Find top-level keys that exist in newObj but not in oldObj
|
|
16
15
|
*/
|
|
17
|
-
function
|
|
18
|
-
const
|
|
19
|
-
for (const key of Object.keys(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
} else
|
|
24
|
-
|
|
16
|
+
function findNewKeys(newObj, oldObj, prefix = '') {
|
|
17
|
+
const newKeys = [];
|
|
18
|
+
for (const key of Object.keys(newObj)) {
|
|
19
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
20
|
+
if (!(key in oldObj)) {
|
|
21
|
+
newKeys.push(fullKey);
|
|
22
|
+
} else if (
|
|
23
|
+
newObj[key] !== null && typeof newObj[key] === 'object' && !Array.isArray(newObj[key]) &&
|
|
24
|
+
oldObj[key] !== null && typeof oldObj[key] === 'object' && !Array.isArray(oldObj[key])
|
|
25
|
+
) {
|
|
26
|
+
newKeys.push(...findNewKeys(newObj[key], oldObj[key], fullKey));
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
|
-
return
|
|
29
|
+
return newKeys;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -278,6 +280,8 @@ program
|
|
|
278
280
|
let hadVectorizer = false;
|
|
279
281
|
let hadVectors = false;
|
|
280
282
|
|
|
283
|
+
let existingConfigContent = null;
|
|
284
|
+
|
|
281
285
|
if (await fs.pathExists(targetDir)) {
|
|
282
286
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
283
287
|
const backupDir = path.join(process.cwd(), `.opencode.backup-${timestamp}`);
|
|
@@ -286,6 +290,12 @@ program
|
|
|
286
290
|
hadVectorizer = await fs.pathExists(vectorizerNodeModules);
|
|
287
291
|
hadVectors = await fs.pathExists(vectorsDir);
|
|
288
292
|
|
|
293
|
+
// Read existing config.yaml for merge
|
|
294
|
+
const existingConfigPath = path.join(targetDir, 'config.yaml');
|
|
295
|
+
if (await fs.pathExists(existingConfigPath)) {
|
|
296
|
+
existingConfigContent = await fs.readFile(existingConfigPath, 'utf8');
|
|
297
|
+
}
|
|
298
|
+
|
|
289
299
|
// Preserve vectorizer node_modules (100MB+, don't backup)
|
|
290
300
|
if (hadVectorizer) {
|
|
291
301
|
spinner.text = 'Preserving vectorizer dependencies...';
|
|
@@ -339,20 +349,31 @@ program
|
|
|
339
349
|
// Update config.yaml with user values
|
|
340
350
|
spinner.text = 'Configuring...';
|
|
341
351
|
const configPath = path.join(targetDir, 'config.yaml');
|
|
342
|
-
let configContent
|
|
352
|
+
let configContent;
|
|
353
|
+
|
|
354
|
+
// If we had existing config, use it as base (preserves comments and formatting)
|
|
355
|
+
if (existingConfigContent) {
|
|
356
|
+
configContent = existingConfigContent;
|
|
357
|
+
// Update version to match new package
|
|
358
|
+
configContent = configContent.replace(/^version:\s*["']?[\d.]+["']?/m, `version: "${VERSION}"`);
|
|
359
|
+
console.log(chalk.green(' ✅ Restored your config (comments preserved)'));
|
|
360
|
+
} else {
|
|
361
|
+
configContent = await fs.readFile(configPath, 'utf8');
|
|
362
|
+
}
|
|
343
363
|
|
|
364
|
+
// Apply user's answers from prompts
|
|
344
365
|
configContent = configContent
|
|
345
|
-
.replace(/user_name
|
|
346
|
-
.replace(/communication_language
|
|
347
|
-
.replace(/project_name
|
|
348
|
-
.replace(/methodology
|
|
366
|
+
.replace(/user_name:\s*["']?[^"\n]*["']?/, `user_name: "${config.user_name}"`)
|
|
367
|
+
.replace(/communication_language:\s*["']?[^"\n]*["']?/, `communication_language: "${config.communication_language}"`)
|
|
368
|
+
.replace(/project_name:\s*["']?[^"\n]*["']?/, `project_name: "${config.project_name}"`)
|
|
369
|
+
.replace(/methodology:\s*(tdd|stub)/, `methodology: ${config.methodology}`);
|
|
349
370
|
|
|
350
371
|
// Jira config
|
|
351
372
|
if (config.jira_enabled) {
|
|
352
373
|
configContent = configContent
|
|
353
374
|
.replace(/enabled: false\s+# Jira/, `enabled: true # Jira`)
|
|
354
|
-
.replace(/base_url:
|
|
355
|
-
.replace(/project_key:
|
|
375
|
+
.replace(/base_url: .*/, `base_url: "${config.jira_url}"`)
|
|
376
|
+
.replace(/project_key: .*/, `project_key: "${config.jira_project}"`);
|
|
356
377
|
}
|
|
357
378
|
|
|
358
379
|
// Vectorizer config
|
|
@@ -590,33 +611,37 @@ program
|
|
|
590
611
|
await fs.move(tempVectors, path.join(targetDir, 'vectors'), { overwrite: true });
|
|
591
612
|
}
|
|
592
613
|
|
|
593
|
-
//
|
|
594
|
-
spinner.text = '
|
|
614
|
+
// Restore user's config.yaml (preserves comments and formatting)
|
|
615
|
+
spinner.text = 'Restoring config.yaml...';
|
|
595
616
|
try {
|
|
596
|
-
|
|
597
|
-
|
|
617
|
+
// Update version in user's config to match new package
|
|
618
|
+
let restoredConfig = configBackup.replace(
|
|
619
|
+
/^version:\s*["']?[\d.]+["']?/m,
|
|
620
|
+
`version: "${VERSION}"`
|
|
621
|
+
);
|
|
622
|
+
await fs.writeFile(configPath, restoredConfig);
|
|
623
|
+
console.log(chalk.green(' ✅ config.yaml restored (your settings + comments preserved)'));
|
|
598
624
|
|
|
599
|
-
//
|
|
600
|
-
const
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
console.log(chalk.green(' ✅ config.yaml merged (your settings preserved, new options added)'));
|
|
625
|
+
// Check if new template has options that user doesn't have
|
|
626
|
+
const newConfigPath = path.join(OPENCODE_SRC, 'config.yaml');
|
|
627
|
+
if (await fs.pathExists(newConfigPath)) {
|
|
628
|
+
const newConfig = yaml.load(await fs.readFile(newConfigPath, 'utf8')) || {};
|
|
629
|
+
const userConfig = yaml.load(configBackup) || {};
|
|
630
|
+
const newKeys = findNewKeys(newConfig, userConfig);
|
|
631
|
+
if (newKeys.length > 0) {
|
|
632
|
+
console.log(chalk.yellow(` 💡 New config options available: ${newKeys.slice(0, 3).join(', ')}${newKeys.length > 3 ? '...' : ''}`));
|
|
633
|
+
console.log(chalk.gray(' Check .opencode.backup-*/config.yaml.new for full template'));
|
|
634
|
+
// Save new template for reference
|
|
635
|
+
await fs.writeFile(
|
|
636
|
+
path.join(process.cwd(), `.opencode.backup-${timestamp}`, 'config.yaml.new'),
|
|
637
|
+
await fs.readFile(newConfigPath, 'utf8')
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
616
641
|
} catch (e) {
|
|
617
|
-
// Fallback: just restore user's config
|
|
642
|
+
// Fallback: just restore user's config
|
|
618
643
|
await fs.writeFile(configPath, configBackup);
|
|
619
|
-
console.log(chalk.yellow(' ⚠️ config.yaml restored
|
|
644
|
+
console.log(chalk.yellow(' ⚠️ config.yaml restored'));
|
|
620
645
|
}
|
|
621
646
|
|
|
622
647
|
// Install plugin dependencies
|
package/package.json
CHANGED