dev-playbooks-cn 2.0.0 → 2.1.1
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/README.md +1 -1
- package/bin/devbooks.js +200 -5
- package/package.json +1 -1
- package/templates/dev-playbooks/README.md +1 -1
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ AI 编码助手很强大,但往往**不可预测**:
|
|
|
79
79
|
| **Claude Code** | 完整 Skills | `CLAUDE.md` |
|
|
80
80
|
| **Codex CLI** | 完整 Skills | `AGENTS.md` |
|
|
81
81
|
| **Qoder** | 完整 Skills | `AGENTS.md` |
|
|
82
|
-
| **OpenCode
|
|
82
|
+
| **OpenCode** | 完整 Skills | `AGENTS.md` |
|
|
83
83
|
| **Cursor** | Rules 系统 | `.cursor/rules/` |
|
|
84
84
|
| **Windsurf** | Rules 系统 | `.windsurf/rules/` |
|
|
85
85
|
| **Gemini CLI** | Rules 系统 | `GEMINI.md` |
|
package/bin/devbooks.js
CHANGED
|
@@ -84,7 +84,7 @@ const AI_TOOLS = [
|
|
|
84
84
|
{
|
|
85
85
|
id: 'opencode',
|
|
86
86
|
name: 'OpenCode',
|
|
87
|
-
description: 'OpenCode AI CLI
|
|
87
|
+
description: 'OpenCode AI CLI',
|
|
88
88
|
skillsSupport: SKILLS_SUPPORT.FULL,
|
|
89
89
|
slashDir: '.opencode/command',
|
|
90
90
|
agentsDir: '.opencode/agent',
|
|
@@ -380,6 +380,195 @@ async function performNpmUpdate() {
|
|
|
380
380
|
});
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
+
/**
|
|
384
|
+
* 显示版本变更摘要
|
|
385
|
+
* @param {string} fromVersion - 当前版本
|
|
386
|
+
* @param {string} toVersion - 目标版本
|
|
387
|
+
*/
|
|
388
|
+
async function displayVersionChangelog(fromVersion, toVersion) {
|
|
389
|
+
try {
|
|
390
|
+
// 尝试从 npm 获取 CHANGELOG
|
|
391
|
+
const { execSync } = await import('child_process');
|
|
392
|
+
const changelogUrl = `https://raw.githubusercontent.com/Darkbluelr/dev-playbooks-cn/master/CHANGELOG.md`;
|
|
393
|
+
|
|
394
|
+
// 使用 curl 获取 CHANGELOG(如果可用)
|
|
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
|
+
// 如果获取失败,显示简化信息
|
|
403
|
+
console.log(chalk.cyan('📋 版本变更摘要'));
|
|
404
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
405
|
+
console.log(chalk.yellow('⚠ 无法获取详细变更日志,请访问:'));
|
|
406
|
+
console.log(chalk.blue(` https://github.com/Darkbluelr/dev-playbooks-cn/releases/tag/v${toVersion}`));
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// 解析 CHANGELOG,提取相关版本的变更
|
|
411
|
+
const changes = parseChangelog(changelog, fromVersion, toVersion);
|
|
412
|
+
|
|
413
|
+
if (changes.length === 0) {
|
|
414
|
+
console.log(chalk.cyan('📋 版本变更摘要'));
|
|
415
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
416
|
+
console.log(chalk.yellow('⚠ 未找到详细变更信息,请访问:'));
|
|
417
|
+
console.log(chalk.blue(` https://github.com/Darkbluelr/dev-playbooks-cn/releases/tag/v${toVersion}`));
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// 显示变更摘要
|
|
422
|
+
console.log(chalk.cyan('📋 版本变更摘要'));
|
|
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(` 发布日期: ${change.date}`));
|
|
430
|
+
}
|
|
431
|
+
console.log();
|
|
432
|
+
|
|
433
|
+
// 显示主要变更(限制显示前10条)
|
|
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(' ... (更多变更请查看完整日志)'));
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
console.log();
|
|
460
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
461
|
+
console.log(chalk.blue('📖 完整变更日志: ') + chalk.underline(`https://github.com/Darkbluelr/dev-playbooks-cn/blob/master/CHANGELOG.md`));
|
|
462
|
+
|
|
463
|
+
} catch (error) {
|
|
464
|
+
// 静默失败,不影响更新流程
|
|
465
|
+
console.log(chalk.gray('提示: 无法显示变更摘要'));
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* 解析 CHANGELOG 内容,提取指定版本范围的变更
|
|
471
|
+
* @param {string} changelog - CHANGELOG 内容
|
|
472
|
+
* @param {string} fromVersion - 起始版本
|
|
473
|
+
* @param {string} toVersion - 目标版本
|
|
474
|
+
* @returns {Array} - 变更列表
|
|
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
|
+
// 解析版本号(移除 'v' 前缀)
|
|
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
|
+
// 匹配版本标题:## [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
|
+
// 保存上一个版本的内容
|
|
498
|
+
if (inVersionBlock && shouldCapture && currentVersion) {
|
|
499
|
+
changes.push({
|
|
500
|
+
version: currentVersion,
|
|
501
|
+
date: currentDate,
|
|
502
|
+
content: currentContent.join('\n').trim()
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// 开始新版本
|
|
507
|
+
currentVersion = versionMatch[1];
|
|
508
|
+
currentDate = versionMatch[2] || null;
|
|
509
|
+
currentContent = [];
|
|
510
|
+
inVersionBlock = true;
|
|
511
|
+
|
|
512
|
+
// 判断是否应该捕获这个版本
|
|
513
|
+
// 捕获从 fromVersion 到 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
|
+
// 如果遇到下一个版本标题或分隔线,结束当前版本
|
|
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
|
+
// 收集内容
|
|
541
|
+
if (inVersionBlock && shouldCapture) {
|
|
542
|
+
currentContent.push(line);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// 保存最后一个版本
|
|
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
|
+
* 比较两个版本号
|
|
560
|
+
* @param {number[]} v1 - 版本1 [major, minor, patch]
|
|
561
|
+
* @param {number[]} v2 - 版本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
|
// 自动更新 .gitignore 和 .npmignore
|
|
385
574
|
// ============================================================================
|
|
@@ -681,7 +870,7 @@ function installSkills(toolIds, projectDir, scope = INSTALL_SCOPE.GLOBAL, update
|
|
|
681
870
|
const tool = AI_TOOLS.find(t => t.id === toolId);
|
|
682
871
|
if (!tool || tool.skillsSupport !== SKILLS_SUPPORT.FULL) continue;
|
|
683
872
|
|
|
684
|
-
// Claude Code / Codex CLI / OpenCode
|
|
873
|
+
// Claude Code / Codex CLI / OpenCode 支持相同格式的 Skills
|
|
685
874
|
if ((toolId === 'claude' || toolId === 'codex' || toolId === 'opencode') && tool.skillsDir) {
|
|
686
875
|
const skillsSrcDir = path.join(__dirname, '..', 'skills');
|
|
687
876
|
const skillsDestDir = getSkillsDestDir(tool, scope, projectDir);
|
|
@@ -751,11 +940,11 @@ function installSkills(toolIds, projectDir, scope = INSTALL_SCOPE.GLOBAL, update
|
|
|
751
940
|
|
|
752
941
|
function generateOpenCodeDevbooksCommand() {
|
|
753
942
|
return `---
|
|
754
|
-
description: DevBooks
|
|
943
|
+
description: DevBooks 工作流入口(OpenCode)
|
|
755
944
|
---
|
|
756
945
|
|
|
757
946
|
${DEVBOOKS_MARKERS.start}
|
|
758
|
-
# DevBooks
|
|
947
|
+
# DevBooks(OpenCode)
|
|
759
948
|
|
|
760
949
|
本项目使用 DevBooks 工作流进行规格驱动开发。
|
|
761
950
|
|
|
@@ -764,7 +953,7 @@ ${DEVBOOKS_MARKERS.start}
|
|
|
764
953
|
1. **推荐入口(Router)**:在对话中输入:\`/devbooks-router\`
|
|
765
954
|
2. 或使用自然语言:\`请运行 devbooks-router skill,分析需求:<你的需求>\`
|
|
766
955
|
|
|
767
|
-
> 说明:在
|
|
956
|
+
> 说明:在 OpenCode 中,Skills 会作为可用的 Slash Commands 被加载,因此可以直接用 \`/<skill-name>\` 调用。
|
|
768
957
|
|
|
769
958
|
## 常用命令(直接用 /<skill-name>)
|
|
770
959
|
|
|
@@ -1373,6 +1562,12 @@ async function updateCommand(projectDir) {
|
|
|
1373
1562
|
|
|
1374
1563
|
if (hasUpdate) {
|
|
1375
1564
|
spinner.info(`发现新版本: ${currentVersion} → ${latestVersion}`);
|
|
1565
|
+
|
|
1566
|
+
// 显示版本变更摘要
|
|
1567
|
+
console.log();
|
|
1568
|
+
await displayVersionChangelog(currentVersion, latestVersion);
|
|
1569
|
+
console.log();
|
|
1570
|
+
|
|
1376
1571
|
const shouldUpdate = await confirm({
|
|
1377
1572
|
message: `是否更新 ${CLI_COMMAND} 到 ${latestVersion}?`,
|
|
1378
1573
|
default: true
|
package/package.json
CHANGED
|
@@ -61,7 +61,7 @@ AI 编码助手很强大,但往往**不可预测**:
|
|
|
61
61
|
| **Claude Code** | 完整 Skills | `CLAUDE.md` |
|
|
62
62
|
| **Codex CLI** | 完整 Skills | `AGENTS.md` |
|
|
63
63
|
| **Qoder** | 完整 Skills | `AGENTS.md` |
|
|
64
|
-
| **OpenCode
|
|
64
|
+
| **OpenCode** | 完整 Skills | `AGENTS.md` |
|
|
65
65
|
| **Cursor** | Rules 系统 | `.cursor/rules/` |
|
|
66
66
|
| **Windsurf** | Rules 系统 | `.windsurf/rules/` |
|
|
67
67
|
| **Gemini CLI** | Rules 系统 | `GEMINI.md` |
|