convoke-agents 3.0.1 → 3.0.3

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.
@@ -33,7 +33,7 @@ workflows:
33
33
  - learning-card
34
34
  - pivot-patch-persevere
35
35
  - vortex-navigation
36
- version: 3.0.0
36
+ version: 3.0.2
37
37
  user_name: '{user}'
38
38
  communication_language: en
39
39
  party_mode_enabled: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "convoke-agents",
3
- "version": "3.0.1",
3
+ "version": "3.0.3",
4
4
  "description": "Agent teams for complex systems, compatible with BMad Method",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -147,28 +147,27 @@ function checkModuleConfig(mod) {
147
147
  };
148
148
  }
149
149
 
150
- if (!Array.isArray(mod.config.agents) || mod.config.agents.length === 0) {
151
- return {
152
- name: label,
153
- passed: false,
154
- error: 'config.yaml missing or empty agents section',
155
- fix: `Check ${mod.configPath}`
156
- };
157
- }
150
+ const hasAgents = Array.isArray(mod.config.agents) && mod.config.agents.length > 0;
151
+ const hasWorkflows = Array.isArray(mod.config.workflows) && mod.config.workflows.length > 0;
158
152
 
159
- if (!Array.isArray(mod.config.workflows) || mod.config.workflows.length === 0) {
153
+ // A module must have at least agents or workflows
154
+ if (!hasAgents && !hasWorkflows) {
160
155
  return {
161
156
  name: label,
162
157
  passed: false,
163
- error: 'config.yaml missing or empty workflows section',
158
+ error: 'config.yaml missing both agents and workflows sections',
164
159
  fix: `Check ${mod.configPath}`
165
160
  };
166
161
  }
167
162
 
163
+ const parts = [];
164
+ if (hasAgents) parts.push(`${mod.config.agents.length} agents`);
165
+ if (hasWorkflows) parts.push(`${mod.config.workflows.length} workflows`);
166
+
168
167
  return {
169
168
  name: label,
170
169
  passed: true,
171
- info: `${mod.config.agents.length} agents, ${mod.config.workflows.length} workflows`
170
+ info: parts.join(', ')
172
171
  };
173
172
  }
174
173
 
@@ -54,7 +54,7 @@ function assessUpdate(projectRoot) {
54
54
  const breakingChanges = registry.getBreakingChanges(currentVersion);
55
55
 
56
56
  if (migrations.length === 0) {
57
- return { action: 'no-migrations', currentVersion, targetVersion };
57
+ return { action: 'refresh-only', currentVersion, targetVersion };
58
58
  }
59
59
 
60
60
  return {
@@ -144,14 +144,59 @@ async function main() {
144
144
  process.exit(1);
145
145
  break;
146
146
 
147
- case 'no-migrations':
148
- console.log(chalk.yellow('No migrations needed (versions compatible)'));
147
+ case 'refresh-only':
148
+ case 'upgrade':
149
+ break; // Continue below
150
+ }
151
+
152
+ // Refresh-only: no migration deltas, just update files to latest version
153
+ if (assessment.action === 'refresh-only') {
154
+ console.log(chalk.cyan('Update Plan:'));
155
+ console.log(` From: ${chalk.red(assessment.currentVersion)}`);
156
+ console.log(` To: ${chalk.green(assessment.targetVersion)}`);
157
+ console.log('');
158
+ console.log(chalk.cyan('No migration deltas needed — refreshing installation files.'));
159
+ console.log('');
160
+
161
+ if (dryRun) {
162
+ console.log(chalk.yellow.bold('DRY RUN — no changes will be made'));
149
163
  console.log('');
150
164
  process.exit(0);
151
- break;
165
+ }
152
166
 
153
- case 'upgrade':
154
- break; // Continue below
167
+ if (!yes) {
168
+ console.log(chalk.cyan('Your data will be backed up automatically.'));
169
+ console.log('');
170
+ const confirmed = await confirm('Proceed with update?');
171
+ if (!confirmed) {
172
+ console.log('');
173
+ console.log(chalk.yellow('Update cancelled.'));
174
+ console.log('');
175
+ process.exit(0);
176
+ }
177
+ }
178
+
179
+ console.log('');
180
+ console.log(chalk.cyan.bold('Starting update...'));
181
+
182
+ try {
183
+ const result = await migrationRunner.runRefreshOnly(assessment.currentVersion, { verbose });
184
+
185
+ console.log('');
186
+ console.log(chalk.green.bold('✓ Update completed successfully!'));
187
+ console.log('');
188
+ console.log(chalk.cyan('Changes applied:'));
189
+ result.changes.forEach(change => {
190
+ console.log(chalk.green(` ✓ ${change}`));
191
+ });
192
+ console.log('');
193
+ console.log(chalk.gray(`Backup location: ${result.backupMetadata.backup_dir}`));
194
+ console.log('');
195
+ } catch (_error) {
196
+ process.exit(1);
197
+ }
198
+
199
+ process.exit(0);
155
200
  }
156
201
 
157
202
  // Show migration plan
@@ -378,8 +378,108 @@ class MigrationError extends Error {
378
378
  }
379
379
  }
380
380
 
381
+ /**
382
+ * Refresh-only update: no migration deltas, just backup + refresh + validate.
383
+ * Used when upgrading between versions that have no registered migration deltas.
384
+ * @param {string} fromVersion - Current installed version
385
+ * @param {object} options - Options { verbose }
386
+ * @returns {Promise<object>} Update result
387
+ */
388
+ async function runRefreshOnly(fromVersion, options = {}) {
389
+ const { verbose = false } = options;
390
+ const toVersion = getPackageVersion();
391
+
392
+ const projectRoot = findProjectRoot();
393
+ if (!projectRoot) {
394
+ throw new Error('Not in a Convoke project. Could not find _bmad/ directory.');
395
+ }
396
+
397
+ // 1. Acquire lock
398
+ await acquireMigrationLock(projectRoot);
399
+
400
+ let backupMetadata = null;
401
+
402
+ try {
403
+ // 2. Create backup
404
+ console.log(chalk.cyan('[1/3] Creating backup...'));
405
+ backupMetadata = await backupManager.createBackup(fromVersion, projectRoot);
406
+ console.log(chalk.green(`✓ Backup created: ${path.basename(backupMetadata.backup_dir)}`));
407
+ console.log('');
408
+
409
+ // 3. Refresh installation
410
+ console.log(chalk.cyan('[2/3] Refreshing installation files...'));
411
+ const changes = await refreshInstallation(projectRoot, { verbose });
412
+ console.log(chalk.green('✓ Installation refreshed'));
413
+ console.log('');
414
+
415
+ // 4. Validate
416
+ console.log(chalk.cyan('[3/3] Validating installation...'));
417
+ const validationResult = await validator.validateInstallation(backupMetadata, projectRoot);
418
+
419
+ validationResult.checks.forEach(check => {
420
+ if (check.passed) {
421
+ console.log(chalk.green(` ✓ ${check.name}`));
422
+ if (check.info || check.warning) {
423
+ console.log(chalk.gray(` ${check.info || check.warning}`));
424
+ }
425
+ } else {
426
+ console.log(chalk.red(` ✗ ${check.name}`));
427
+ if (check.error) {
428
+ console.log(chalk.red(` Error: ${check.error}`));
429
+ }
430
+ }
431
+ });
432
+ console.log('');
433
+
434
+ if (!validationResult.valid) {
435
+ throw new Error('Installation validation failed');
436
+ }
437
+
438
+ console.log(chalk.green('✓ Installation validated'));
439
+ console.log('');
440
+
441
+ // 5. Cleanup old backups
442
+ const deletedCount = await backupManager.cleanupOldBackups(5, projectRoot);
443
+ if (deletedCount > 0) {
444
+ console.log(chalk.green(`✓ Cleaned up ${deletedCount} old backup(s)`));
445
+ }
446
+
447
+ // Release lock
448
+ await releaseMigrationLock(projectRoot);
449
+
450
+ return {
451
+ success: true,
452
+ fromVersion,
453
+ toVersion,
454
+ changes,
455
+ backupMetadata
456
+ };
457
+
458
+ } catch (error) {
459
+ console.error('');
460
+ console.error(chalk.red.bold('✗ Update failed!'));
461
+ console.error(chalk.red(error.message));
462
+ console.error('');
463
+
464
+ if (backupMetadata) {
465
+ console.log(chalk.yellow('Restoring from backup...'));
466
+ try {
467
+ await backupManager.restoreBackup(backupMetadata, projectRoot);
468
+ console.log(chalk.green('✓ Installation restored from backup'));
469
+ } catch (restoreError) {
470
+ console.error(chalk.red('✗ Restore failed!'));
471
+ console.error(chalk.yellow(`Manual restore may be needed from: ${backupMetadata.backup_dir}`));
472
+ }
473
+ }
474
+
475
+ await releaseMigrationLock(projectRoot);
476
+ throw error;
477
+ }
478
+ }
479
+
381
480
  module.exports = {
382
481
  runMigrations,
482
+ runRefreshOnly,
383
483
  previewMigrations,
384
484
  executeMigration,
385
485
  MigrationError
@@ -115,6 +115,13 @@ async function refreshInstallation(projectRoot, options = {}) {
115
115
 
116
116
  if (!isSameRoot) {
117
117
  await fs.copy(packageEnhance, targetEnhance, { overwrite: true });
118
+ // Stamp enhance config version to match package version
119
+ const targetEnhanceConfig = path.join(targetEnhance, 'config.yaml');
120
+ if (fs.existsSync(targetEnhanceConfig)) {
121
+ const ecContent = yaml.load(fs.readFileSync(targetEnhanceConfig, 'utf8'));
122
+ ecContent.version = version;
123
+ fs.writeFileSync(targetEnhanceConfig, yaml.dump(ecContent, { lineWidth: -1 }), 'utf8');
124
+ }
118
125
  changes.push('Refreshed Enhance module: _bmad/bme/_enhance/');
119
126
  if (verbose) console.log(' Refreshed Enhance module: _bmad/bme/_enhance/');
120
127
  } else {