dev-playbooks 2.0.0 → 2.1.0

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.
Files changed (2) hide show
  1. package/bin/devbooks.js +195 -0
  2. package/package.json +1 -1
package/bin/devbooks.js CHANGED
@@ -380,6 +380,195 @@ async function performNpmUpdate() {
380
380
  });
381
381
  }
382
382
 
383
+ /**
384
+ * Display version changelog summary
385
+ * @param {string} fromVersion - Current version
386
+ * @param {string} toVersion - Target version
387
+ */
388
+ async function displayVersionChangelog(fromVersion, toVersion) {
389
+ try {
390
+ // Try to fetch CHANGELOG from GitHub
391
+ const { execSync } = await import('child_process');
392
+ const changelogUrl = `https://raw.githubusercontent.com/Darkbluelr/dev-playbooks/master/CHANGELOG.md`;
393
+
394
+ // Use curl to fetch CHANGELOG (if available)
395
+ let changelog = '';
396
+ try {
397
+ changelog = execSync(`curl -s -m 5 "${changelogUrl}"`, {
398
+ encoding: 'utf-8',
399
+ stdio: ['pipe', 'pipe', 'pipe']
400
+ });
401
+ } catch {
402
+ // If fetch fails, show simplified info
403
+ console.log(chalk.cyan('📋 Version Changelog'));
404
+ console.log(chalk.gray('─'.repeat(60)));
405
+ console.log(chalk.yellow('⚠ Unable to fetch detailed changelog, please visit:'));
406
+ console.log(chalk.blue(` https://github.com/Darkbluelr/dev-playbooks/releases/tag/v${toVersion}`));
407
+ return;
408
+ }
409
+
410
+ // Parse CHANGELOG, extract changes for relevant versions
411
+ const changes = parseChangelog(changelog, fromVersion, toVersion);
412
+
413
+ if (changes.length === 0) {
414
+ console.log(chalk.cyan('📋 Version Changelog'));
415
+ console.log(chalk.gray('─'.repeat(60)));
416
+ console.log(chalk.yellow('⚠ No detailed changelog found, please visit:'));
417
+ console.log(chalk.blue(` https://github.com/Darkbluelr/dev-playbooks/releases/tag/v${toVersion}`));
418
+ return;
419
+ }
420
+
421
+ // Display changelog summary
422
+ console.log(chalk.cyan('📋 Version Changelog'));
423
+ console.log(chalk.gray('─'.repeat(60)));
424
+
425
+ for (const change of changes) {
426
+ console.log();
427
+ console.log(chalk.bold.green(`## ${change.version}`));
428
+ if (change.date) {
429
+ console.log(chalk.gray(` Release date: ${change.date}`));
430
+ }
431
+ console.log();
432
+
433
+ // Display main changes (limit to first 10 lines)
434
+ const highlights = change.content.split('\n')
435
+ .filter(line => line.trim().length > 0)
436
+ .slice(0, 10);
437
+
438
+ for (const line of highlights) {
439
+ if (line.startsWith('###')) {
440
+ console.log(chalk.bold.yellow(line));
441
+ } else if (line.startsWith('####')) {
442
+ console.log(chalk.bold(line));
443
+ } else if (line.startsWith('- ✅') || line.startsWith('- ✓')) {
444
+ console.log(chalk.green(line));
445
+ } else if (line.startsWith('- ⚠️') || line.startsWith('- ❌')) {
446
+ console.log(chalk.yellow(line));
447
+ } else if (line.startsWith('- ')) {
448
+ console.log(chalk.white(line));
449
+ } else {
450
+ console.log(chalk.gray(line));
451
+ }
452
+ }
453
+
454
+ if (change.content.split('\n').length > 10) {
455
+ console.log(chalk.gray(' ... (see full changelog for more)'));
456
+ }
457
+ }
458
+
459
+ console.log();
460
+ console.log(chalk.gray('─'.repeat(60)));
461
+ console.log(chalk.blue('📖 Full changelog: ') + chalk.underline(`https://github.com/Darkbluelr/dev-playbooks/blob/master/CHANGELOG.md`));
462
+
463
+ } catch (error) {
464
+ // Silent failure, don't affect update process
465
+ console.log(chalk.gray('Note: Unable to display changelog summary'));
466
+ }
467
+ }
468
+
469
+ /**
470
+ * Parse CHANGELOG content, extract changes for specified version range
471
+ * @param {string} changelog - CHANGELOG content
472
+ * @param {string} fromVersion - Starting version
473
+ * @param {string} toVersion - Target version
474
+ * @returns {Array} - List of changes
475
+ */
476
+ function parseChangelog(changelog, fromVersion, toVersion) {
477
+ const changes = [];
478
+ const lines = changelog.split('\n');
479
+
480
+ let currentVersion = null;
481
+ let currentDate = null;
482
+ let currentContent = [];
483
+ let inVersionBlock = false;
484
+ let shouldCapture = false;
485
+
486
+ // Parse version numbers (remove 'v' prefix)
487
+ const from = fromVersion.replace(/^v/, '');
488
+ const to = toVersion.replace(/^v/, '');
489
+
490
+ for (let i = 0; i < lines.length; i++) {
491
+ const line = lines[i];
492
+
493
+ // Match version header: ## [2.0.0] - 2026-01-19
494
+ const versionMatch = line.match(/^##\s+\[?(\d+\.\d+\.\d+)\]?\s*(?:-\s*(\d{4}-\d{2}-\d{2}))?/);
495
+
496
+ if (versionMatch) {
497
+ // Save previous version's content
498
+ if (inVersionBlock && shouldCapture && currentVersion) {
499
+ changes.push({
500
+ version: currentVersion,
501
+ date: currentDate,
502
+ content: currentContent.join('\n').trim()
503
+ });
504
+ }
505
+
506
+ // Start new version
507
+ currentVersion = versionMatch[1];
508
+ currentDate = versionMatch[2] || null;
509
+ currentContent = [];
510
+ inVersionBlock = true;
511
+
512
+ // Determine if this version should be captured
513
+ // Capture all versions between fromVersion and toVersion
514
+ const versionNum = currentVersion.split('.').map(Number);
515
+ const fromNum = from.split('.').map(Number);
516
+ const toNum = to.split('.').map(Number);
517
+
518
+ const isAfterFrom = compareVersions(versionNum, fromNum) > 0;
519
+ const isBeforeOrEqualTo = compareVersions(versionNum, toNum) <= 0;
520
+
521
+ shouldCapture = isAfterFrom && isBeforeOrEqualTo;
522
+
523
+ continue;
524
+ }
525
+
526
+ // If we encounter next version header or separator, end current version
527
+ if (line.startsWith('---') && inVersionBlock) {
528
+ if (shouldCapture && currentVersion) {
529
+ changes.push({
530
+ version: currentVersion,
531
+ date: currentDate,
532
+ content: currentContent.join('\n').trim()
533
+ });
534
+ }
535
+ inVersionBlock = false;
536
+ shouldCapture = false;
537
+ continue;
538
+ }
539
+
540
+ // Collect content
541
+ if (inVersionBlock && shouldCapture) {
542
+ currentContent.push(line);
543
+ }
544
+ }
545
+
546
+ // Save last version
547
+ if (inVersionBlock && shouldCapture && currentVersion) {
548
+ changes.push({
549
+ version: currentVersion,
550
+ date: currentDate,
551
+ content: currentContent.join('\n').trim()
552
+ });
553
+ }
554
+
555
+ return changes;
556
+ }
557
+
558
+ /**
559
+ * Compare two version numbers
560
+ * @param {number[]} v1 - Version 1 [major, minor, patch]
561
+ * @param {number[]} v2 - Version 2 [major, minor, patch]
562
+ * @returns {number} - 1 if v1 > v2, -1 if v1 < v2, 0 if equal
563
+ */
564
+ function compareVersions(v1, v2) {
565
+ for (let i = 0; i < 3; i++) {
566
+ if (v1[i] > v2[i]) return 1;
567
+ if (v1[i] < v2[i]) return -1;
568
+ }
569
+ return 0;
570
+ }
571
+
383
572
  // ============================================================================
384
573
  // Auto-update .gitignore and .npmignore
385
574
  // ============================================================================
@@ -1369,6 +1558,12 @@ async function updateCommand(projectDir) {
1369
1558
 
1370
1559
  if (hasUpdate) {
1371
1560
  spinner.info(`New version available: ${currentVersion} → ${latestVersion}`);
1561
+
1562
+ // Display version changelog summary
1563
+ console.log();
1564
+ await displayVersionChangelog(currentVersion, latestVersion);
1565
+ console.log();
1566
+
1372
1567
  const shouldUpdate = await confirm({
1373
1568
  message: `Update ${CLI_COMMAND} to ${latestVersion}?`,
1374
1569
  default: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev-playbooks",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "AI-powered spec-driven development workflow",
5
5
  "keywords": [
6
6
  "devbooks",