dev-playbooks 1.5.0 → 1.5.2

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 CHANGED
@@ -81,11 +81,21 @@ npx dev-playbooks@latest init
81
81
 
82
82
  ### Install targets
83
83
 
84
- After initialization:
85
- - Claude Code: `~/.claude/skills/devbooks-*`
86
- - Codex CLI: `~/.codex/skills/devbooks-*`
87
- - Qoder: `~/.qoder/` (manual setup required)
88
- - OpenCode: `~/.config/opencode/skill/devbooks-*`
84
+ During initialization, you can choose the Skills installation scope:
85
+
86
+ | Scope | Description | Path Example |
87
+ |-------|-------------|--------------|
88
+ | **Project** (default) | Available only in this project | `.claude/skills/devbooks-*` |
89
+ | **Global** | Shared across all projects | `~/.claude/skills/devbooks-*` |
90
+
91
+ ```bash
92
+ # Interactive selection
93
+ dev-playbooks init
94
+
95
+ # Non-interactive
96
+ dev-playbooks init --tools claude --scope project # Project-level
97
+ dev-playbooks init --tools claude --scope global # Global
98
+ ```
89
99
 
90
100
  ### Quick integration
91
101
 
@@ -194,6 +204,7 @@ Run devbooks-archiver skill for change add-oauth2
194
204
  |-------|-------------|
195
205
  | devbooks-entropy-monitor | System entropy metrics |
196
206
  | devbooks-brownfield-bootstrap | Brownfield project bootstrap |
207
+ | devbooks-convergence-audit | Convergence audit (anti-deception design) |
197
208
 
198
209
  ---
199
210
 
package/bin/devbooks.js CHANGED
@@ -24,7 +24,7 @@ import path from 'path';
24
24
  import os from 'os';
25
25
  import { fileURLToPath } from 'url';
26
26
  import { spawn } from 'child_process';
27
- import { checkbox, confirm } from '@inquirer/prompts';
27
+ import { checkbox, confirm, select } from '@inquirer/prompts';
28
28
  import chalk from 'chalk';
29
29
  import ora from 'ora';
30
30
 
@@ -45,6 +45,15 @@ const SKILLS_SUPPORT = {
45
45
  BASIC: 'basic' // 仅基础指令(无独立 Skills 概念)
46
46
  };
47
47
 
48
+ // ============================================================================
49
+ // Skills Install Scope
50
+ // ============================================================================
51
+
52
+ const INSTALL_SCOPE = {
53
+ GLOBAL: 'global', // Global installation (~/.claude/skills etc.)
54
+ PROJECT: 'project' // Project-level installation (.claude/skills etc.)
55
+ };
56
+
48
57
  // ============================================================================
49
58
  // AI 工具配置
50
59
  // ============================================================================
@@ -540,12 +549,68 @@ async function promptToolSelection(projectDir) {
540
549
  return selectedTools;
541
550
  }
542
551
 
552
+ async function promptInstallScope(projectDir, selectedTools) {
553
+ // Check if any tools support full Skills
554
+ const fullSupportTools = selectedTools.filter(id => {
555
+ const tool = AI_TOOLS.find(t => t.id === id);
556
+ return tool && tool.skillsSupport === SKILLS_SUPPORT.FULL;
557
+ });
558
+
559
+ if (fullSupportTools.length === 0) {
560
+ return INSTALL_SCOPE.PROJECT; // No full Skills support tools, default to project-level
561
+ }
562
+
563
+ // Load saved config
564
+ const config = loadConfig(projectDir);
565
+ const savedScope = config.installScope;
566
+
567
+ console.log();
568
+ console.log(chalk.bold('📦 Skills Installation Location'));
569
+ console.log(chalk.gray('─'.repeat(50)));
570
+ console.log();
571
+
572
+ const scope = await select({
573
+ message: 'Where should Skills be installed?',
574
+ choices: [
575
+ {
576
+ name: `Project-level ${chalk.gray('(.claude/skills etc., only for this project)')}`,
577
+ value: INSTALL_SCOPE.PROJECT,
578
+ description: 'Recommended: Skills stay with the project, no impact on other projects'
579
+ },
580
+ {
581
+ name: `Global ${chalk.gray('(~/.claude/skills etc., shared across all projects)')}`,
582
+ value: INSTALL_SCOPE.GLOBAL,
583
+ description: 'All projects share the same set of Skills'
584
+ }
585
+ ],
586
+ default: savedScope || INSTALL_SCOPE.PROJECT
587
+ });
588
+
589
+ return scope;
590
+ }
591
+
543
592
 
544
593
  // ============================================================================
545
- // 安装 SkillsClaude Code, Codex CLI, Qoder
594
+ // Install Skills (Claude Code, Codex CLI, Qoder)
546
595
  // ============================================================================
547
596
 
548
- function installSkills(toolIds, update = false) {
597
+ function getSkillsDestDir(tool, scope, projectDir) {
598
+ // Determine destination directory based on install scope
599
+ if (scope === INSTALL_SCOPE.PROJECT) {
600
+ // Project-level installation: use relative path within project directory
601
+ if (tool.id === 'claude') {
602
+ return path.join(projectDir, '.claude', 'skills');
603
+ } else if (tool.id === 'codex') {
604
+ return path.join(projectDir, '.codex', 'skills');
605
+ } else if (tool.id === 'opencode') {
606
+ return path.join(projectDir, '.opencode', 'skill');
607
+ }
608
+ }
609
+ // Global installation: use tool's defined global directory
610
+ return tool.skillsDir;
611
+ }
612
+
613
+ function installSkills(toolIds, projectDir, scope = INSTALL_SCOPE.GLOBAL, update = false) {
549
614
  const results = [];
550
615
 
551
616
  for (const toolId of toolIds) {
@@ -555,7 +620,7 @@ function installSkills(toolIds, update = false) {
555
620
  // Claude Code / Codex CLI / OpenCode (incl. oh-my-opencode) share the same Skills format
556
621
  if ((toolId === 'claude' || toolId === 'codex' || toolId === 'opencode') && tool.skillsDir) {
557
622
  const skillsSrcDir = path.join(__dirname, '..', 'skills');
558
- const skillsDestDir = tool.skillsDir;
623
+ const skillsDestDir = getSkillsDestDir(tool, scope, projectDir);
559
624
 
560
625
  if (!fs.existsSync(skillsSrcDir)) continue;
561
626
 
@@ -601,13 +666,15 @@ function installSkills(toolIds, update = false) {
601
666
  type: 'skills',
602
667
  count: installedCount,
603
668
  total: skillDirs.length,
604
- removed: removedCount
669
+ removed: removedCount,
670
+ scope: scope,
671
+ path: skillsDestDir
605
672
  });
606
673
  }
607
674
 
608
- // Qoder: 创建 agents 目录结构(但不复制 Skills,因为格式不同)
675
+ // Qoder: Create agents directory structure (but don't copy Skills, different format)
609
676
  if (toolId === 'qoder') {
610
- results.push({ tool: 'Qoder', type: 'agents', count: 0, total: 0, note: '需要手动创建 agents/' });
677
+ results.push({ tool: 'Qoder', type: 'agents', count: 0, total: 0, note: 'Manual agents/ creation required' });
611
678
  }
612
679
  }
613
680
 
@@ -1006,29 +1073,40 @@ function createProjectStructure(projectDir) {
1006
1073
  }
1007
1074
 
1008
1075
  // ============================================================================
1009
- // 保存配置
1076
+ // Save Configuration
1010
1077
  // ============================================================================
1011
1078
 
1012
- function saveConfig(toolIds, projectDir) {
1079
+ function saveConfig(toolIds, projectDir, installScope = INSTALL_SCOPE.PROJECT) {
1013
1080
  const configPath = path.join(projectDir, '.devbooks', 'config.yaml');
1014
1081
 
1015
- // 读取现有配置或创建新配置
1082
+ // Read existing config or create new
1016
1083
  let configContent = '';
1017
1084
  if (fs.existsSync(configPath)) {
1018
1085
  configContent = fs.readFileSync(configPath, 'utf-8');
1019
1086
  }
1020
1087
 
1021
- // 更新 ai_tools 部分
1088
+ // Update ai_tools section
1022
1089
  const toolsYaml = `ai_tools:\n${toolIds.map(id => ` - ${id}`).join('\n')}`;
1023
1090
 
1024
1091
  if (configContent.includes('ai_tools:')) {
1025
- // 替换现有的 ai_tools 部分
1092
+ // Replace existing ai_tools section
1026
1093
  configContent = configContent.replace(/ai_tools:[\s\S]*?(?=\n\w|\n$|$)/, toolsYaml + '\n');
1027
1094
  } else {
1028
- // 追加 ai_tools 部分
1095
+ // Append ai_tools section
1029
1096
  configContent = configContent.trimEnd() + '\n\n' + toolsYaml + '\n';
1030
1097
  }
1031
1098
 
1099
+ // Update install_scope section
1100
+ const scopeYaml = `install_scope: ${installScope}`;
1101
+
1102
+ if (configContent.includes('install_scope:')) {
1103
+ // Replace existing install_scope section
1104
+ configContent = configContent.replace(/install_scope:.*/, scopeYaml);
1105
+ } else {
1106
+ // Append install_scope section
1107
+ configContent = configContent.trimEnd() + '\n\n' + scopeYaml + '\n';
1108
+ }
1109
+
1032
1110
  fs.writeFileSync(configPath, configContent);
1033
1111
  }
1034
1112
 
@@ -1036,38 +1114,42 @@ function loadConfig(projectDir) {
1036
1114
  const configPath = path.join(projectDir, '.devbooks', 'config.yaml');
1037
1115
 
1038
1116
  if (!fs.existsSync(configPath)) {
1039
- return { aiTools: [] };
1117
+ return { aiTools: [], installScope: null };
1040
1118
  }
1041
1119
 
1042
1120
  const content = fs.readFileSync(configPath, 'utf-8');
1043
- const match = content.match(/ai_tools:\s*([\s\S]*?)(?=\n\w|\n$|$)/);
1044
-
1045
- if (!match) {
1046
- return { aiTools: [] };
1047
- }
1048
1121
 
1049
- const tools = match[1]
1050
- .split('\n')
1051
- .map(line => line.trim())
1052
- .filter(line => line.startsWith('-'))
1053
- .map(line => line.replace(/^-\s*/, '').trim());
1054
-
1055
- return { aiTools: tools };
1122
+ // Parse ai_tools
1123
+ const toolsMatch = content.match(/ai_tools:\s*([\s\S]*?)(?=\n\w|\n$|$)/);
1124
+ const tools = toolsMatch
1125
+ ? toolsMatch[1]
1126
+ .split('\n')
1127
+ .map(line => line.trim())
1128
+ .filter(line => line.startsWith('-'))
1129
+ .map(line => line.replace(/^-\s*/, '').trim())
1130
+ : [];
1131
+
1132
+ // Parse install_scope
1133
+ const scopeMatch = content.match(/install_scope:\s*(\w+)/);
1134
+ const installScope = scopeMatch ? scopeMatch[1] : null;
1135
+
1136
+ return { aiTools: tools, installScope };
1056
1137
  }
1057
1138
 
1058
1139
  // ============================================================================
1059
- // Init 命令
1140
+ // Init Command
1060
1141
  // ============================================================================
1061
1142
 
1062
1143
  async function initCommand(projectDir, options) {
1063
1144
  console.log();
1064
1145
  console.log(chalk.cyan('╔══════════════════════════════════════╗'));
1065
- console.log(chalk.cyan('║') + chalk.bold(' DevBooks 初始化向导 ') + chalk.cyan('║'));
1146
+ console.log(chalk.cyan('║') + chalk.bold(' DevBooks Initialization ') + chalk.cyan('║'));
1066
1147
  console.log(chalk.cyan('╚══════════════════════════════════════╝'));
1067
1148
  console.log();
1068
1149
 
1069
- // 确定选择的工具
1150
+ // Determine selected tools
1070
1151
  let selectedTools;
1152
+ let installScope = INSTALL_SCOPE.PROJECT; // Default to project-level installation
1071
1153
 
1072
1154
  if (options.tools) {
1073
1155
  if (options.tools === 'all') {
@@ -1079,27 +1161,35 @@ async function initCommand(projectDir, options) {
1079
1161
  AI_TOOLS.some(tool => tool.id === t)
1080
1162
  );
1081
1163
  }
1082
- console.log(chalk.blue('ℹ') + ` 非交互式模式:${selectedTools.length > 0 ? selectedTools.join(', ') : ''}`);
1164
+ console.log(chalk.blue('ℹ') + ` Non-interactive mode: ${selectedTools.length > 0 ? selectedTools.join(', ') : 'none'}`);
1165
+
1166
+ // In non-interactive mode, check --scope option
1167
+ if (options.scope) {
1168
+ installScope = options.scope === 'global' ? INSTALL_SCOPE.GLOBAL : INSTALL_SCOPE.PROJECT;
1169
+ }
1083
1170
  } else {
1084
1171
  selectedTools = await promptToolSelection(projectDir);
1172
+
1173
+ // Interactive scope selection
1174
+ installScope = await promptInstallScope(projectDir, selectedTools);
1085
1175
  }
1086
1176
 
1087
- // 创建项目结构
1088
- const spinner = ora('创建项目结构...').start();
1177
+ // Create project structure
1178
+ const spinner = ora('Creating project structure...').start();
1089
1179
  const templateCount = createProjectStructure(projectDir);
1090
- spinner.succeed(`创建了 ${templateCount} 个模板文件`);
1180
+ spinner.succeed(`Created ${templateCount} template files`);
1091
1181
 
1092
- // 保存配置
1093
- saveConfig(selectedTools, projectDir);
1182
+ // Save configuration (including install scope)
1183
+ saveConfig(selectedTools, projectDir, installScope);
1094
1184
 
1095
1185
  if (selectedTools.length === 0) {
1096
1186
  console.log();
1097
- console.log(chalk.green('✓') + ' DevBooks 项目结构已创建!');
1098
- console.log(chalk.gray(` 运行 \`${CLI_COMMAND} init\` 并选择 AI 工具来配置集成。`));
1187
+ console.log(chalk.green('✓') + ' DevBooks project structure created!');
1188
+ console.log(chalk.gray(` Run \`${CLI_COMMAND} init\` and select AI tools to configure integration.`));
1099
1189
  return;
1100
1190
  }
1101
1191
 
1102
- // 安装 Skills(仅完整支持的工具)
1192
+ // Install Skills (only for full support tools)
1103
1193
  const fullSupportTools = selectedTools.filter(id => {
1104
1194
  const tool = AI_TOOLS.find(t => t.id === id);
1105
1195
  return tool && tool.skillsSupport === SKILLS_SUPPORT.FULL;
@@ -1107,12 +1197,16 @@ async function initCommand(projectDir, options) {
1107
1197
 
1108
1198
  if (fullSupportTools.length > 0) {
1109
1199
  const skillsSpinner = ora('Installing Skills...').start();
1110
- const skillsResults = installSkills(fullSupportTools);
1200
+ const skillsResults = installSkills(fullSupportTools, projectDir, installScope);
1111
1201
  skillsSpinner.succeed('Skills installed');
1112
1202
 
1113
1203
  for (const result of skillsResults) {
1114
1204
  if (result.count > 0) {
1115
- console.log(chalk.gray(` └ ${result.tool}: ${result.count}/${result.total} ${result.type}`));
1205
+ const scopeLabel = result.scope === INSTALL_SCOPE.PROJECT ? 'project-level' : 'global';
1206
+ console.log(chalk.gray(` └ ${result.tool}: ${result.count}/${result.total} ${result.type} (${scopeLabel})`));
1207
+ if (result.path) {
1208
+ console.log(chalk.gray(` → ${result.path}`));
1209
+ }
1116
1210
  } else if (result.note) {
1117
1211
  console.log(chalk.gray(` └ ${result.tool}: ${result.note}`));
1118
1212
  }
@@ -1127,14 +1221,14 @@ async function initCommand(projectDir, options) {
1127
1221
  }
1128
1222
  }
1129
1223
 
1130
- // 安装 RulesRules 类似系统的工具)
1224
+ // Install Rules (for Rules-like system tools)
1131
1225
  const rulesTools = selectedTools.filter(id => {
1132
1226
  const tool = AI_TOOLS.find(t => t.id === id);
1133
1227
  return tool && tool.skillsSupport === SKILLS_SUPPORT.RULES;
1134
1228
  });
1135
1229
 
1136
1230
  if (rulesTools.length > 0) {
1137
- const rulesSpinner = ora('安装 Rules...').start();
1231
+ const rulesSpinner = ora('Installing Rules...').start();
1138
1232
  const rulesResults = installRules(rulesTools, projectDir);
1139
1233
  rulesSpinner.succeed(`创建了 ${rulesResults.length} 个规则文件`);
1140
1234
 
@@ -1213,6 +1307,7 @@ async function updateCommand(projectDir) {
1213
1307
  // Load config
1214
1308
  const config = loadConfig(projectDir);
1215
1309
  const configuredTools = config.aiTools;
1310
+ const installScope = config.installScope || INSTALL_SCOPE.PROJECT;
1216
1311
 
1217
1312
  if (configuredTools.length === 0) {
1218
1313
  console.log(chalk.yellow('⚠') + ` No AI tools configured. Run \`${CLI_COMMAND} init\` to configure.`);
@@ -1223,13 +1318,17 @@ async function updateCommand(projectDir) {
1223
1318
  const tool = AI_TOOLS.find(t => t.id === id);
1224
1319
  return tool ? tool.name : id;
1225
1320
  });
1226
- console.log(chalk.blue('ℹ') + ` Detected configured tools: ${toolNames.join(', ')}`);
1321
+ const scopeLabel = installScope === INSTALL_SCOPE.PROJECT ? 'project-level' : 'global';
1322
+ console.log(chalk.blue('ℹ') + ` Detected configured tools: ${toolNames.join(', ')} (${scopeLabel} installation)`);
1227
1323
 
1228
- // Update Skills (global directory)
1229
- const skillsResults = installSkills(configuredTools, true);
1324
+ // Update Skills (using saved install scope from config)
1325
+ const skillsResults = installSkills(configuredTools, projectDir, installScope, true);
1230
1326
  for (const result of skillsResults) {
1231
1327
  if (result.count > 0) {
1232
1328
  console.log(chalk.green('✓') + ` ${result.tool} ${result.type}: updated ${result.count}/${result.total}`);
1329
+ if (result.path) {
1330
+ console.log(chalk.gray(` → ${result.path}`));
1331
+ }
1233
1332
  }
1234
1333
  if (result.removed && result.removed > 0) {
1235
1334
  console.log(chalk.green('✓') + ` ${result.tool} ${result.type}: removed ${result.removed} obsolete skills`);
@@ -1356,31 +1455,33 @@ async function migrateCommand(projectDir, options) {
1356
1455
  }
1357
1456
 
1358
1457
  // ============================================================================
1359
- // 帮助信息
1458
+ // Help Information
1360
1459
  // ============================================================================
1361
1460
 
1362
1461
  function showHelp() {
1363
1462
  console.log();
1364
1463
  console.log(chalk.bold('DevBooks') + ' - AI-agnostic spec-driven development workflow');
1365
1464
  console.log();
1366
- console.log(chalk.cyan('用法:'));
1367
- console.log(` ${CLI_COMMAND} init [path] [options] 初始化 DevBooks`);
1368
- console.log(` ${CLI_COMMAND} update [path] 更新已配置的工具`);
1369
- console.log(` ${CLI_COMMAND} migrate --from <framework> [opts] 从其他框架迁移`);
1465
+ console.log(chalk.cyan('Usage:'));
1466
+ console.log(` ${CLI_COMMAND} init [path] [options] Initialize DevBooks`);
1467
+ console.log(` ${CLI_COMMAND} update [path] Update configured tools`);
1468
+ console.log(` ${CLI_COMMAND} migrate --from <framework> [opts] Migrate from other frameworks`);
1370
1469
  console.log();
1371
- console.log(chalk.cyan('选项:'));
1372
- console.log(' --tools <tools> 非交互式指定 AI 工具');
1373
- console.log(' 可用值: all, none, 或逗号分隔的工具 ID');
1374
- console.log(' --from <framework> 迁移来源框架 (openspec, speckit)');
1375
- console.log(' --dry-run 模拟运行,不实际修改文件');
1376
- console.log(' --keep-old 迁移后保留原目录');
1377
- console.log(' --force 强制重新执行所有步骤');
1378
- console.log(' -h, --help 显示此帮助信息');
1470
+ console.log(chalk.cyan('Options:'));
1471
+ console.log(' --tools <tools> Non-interactive AI tool selection');
1472
+ console.log(' Values: all, none, or comma-separated tool IDs');
1473
+ console.log(' --scope <scope> Skills installation location (non-interactive mode)');
1474
+ console.log(' Values: project (default), global');
1475
+ console.log(' --from <framework> Migration source framework (openspec, speckit)');
1476
+ console.log(' --dry-run Dry run, no actual file modifications');
1477
+ console.log(' --keep-old Keep original directory after migration');
1478
+ console.log(' --force Force re-execute all steps');
1479
+ console.log(' -h, --help Show this help message');
1379
1480
  console.log(' -v, --version Show version');
1380
1481
  console.log();
1381
- console.log(chalk.cyan('支持的 AI 工具:'));
1482
+ console.log(chalk.cyan('Supported AI Tools:'));
1382
1483
 
1383
- // Skills 支持级别分组显示
1484
+ // Group tools by Skills support level
1384
1485
  const groupedTools = {
1385
1486
  [SKILLS_SUPPORT.FULL]: [],
1386
1487
  [SKILLS_SUPPORT.RULES]: [],
@@ -1393,43 +1494,44 @@ function showHelp() {
1393
1494
  }
1394
1495
 
1395
1496
  console.log();
1396
- console.log(chalk.green(' ★ 完整 Skills 支持:'));
1497
+ console.log(chalk.green(' ★ Full Skills Support:'));
1397
1498
  for (const tool of groupedTools[SKILLS_SUPPORT.FULL]) {
1398
1499
  console.log(` ${tool.id.padEnd(15)} ${tool.name}`);
1399
1500
  }
1400
1501
 
1401
1502
  console.log();
1402
- console.log(chalk.blue(' ◆ Rules 系统支持:'));
1503
+ console.log(chalk.blue(' ◆ Rules System Support:'));
1403
1504
  for (const tool of groupedTools[SKILLS_SUPPORT.RULES]) {
1404
1505
  console.log(` ${tool.id.padEnd(15)} ${tool.name}`);
1405
1506
  }
1406
1507
 
1407
1508
  console.log();
1408
- console.log(chalk.yellow(' ● 自定义指令支持:'));
1509
+ console.log(chalk.yellow(' ● Custom Instructions Support:'));
1409
1510
  for (const tool of groupedTools[SKILLS_SUPPORT.AGENTS]) {
1410
1511
  console.log(` ${tool.id.padEnd(15)} ${tool.name}`);
1411
1512
  }
1412
1513
 
1413
1514
  console.log();
1414
1515
  console.log();
1415
- console.log(chalk.cyan('示例:'));
1416
- console.log(` ${CLI_COMMAND} init # 交互式初始化`);
1417
- console.log(` ${CLI_COMMAND} init my-project # my-project 目录初始化`);
1418
- console.log(` ${CLI_COMMAND} init --tools claude,cursor # 非交互式`);
1419
- console.log(` ${CLI_COMMAND} update # 更新已配置的工具`);
1420
- console.log(` ${CLI_COMMAND} migrate --from openspec # OpenSpec 迁移`);
1421
- console.log(` ${CLI_COMMAND} migrate --from speckit # spec-kit 迁移`);
1422
- console.log(` ${CLI_COMMAND} migrate --from openspec --dry-run # 模拟迁移`);
1516
+ console.log(chalk.cyan('Examples:'));
1517
+ console.log(` ${CLI_COMMAND} init # Interactive initialization`);
1518
+ console.log(` ${CLI_COMMAND} init my-project # Initialize in my-project directory`);
1519
+ console.log(` ${CLI_COMMAND} init --tools claude,cursor # Non-interactive (project-level by default)`);
1520
+ console.log(` ${CLI_COMMAND} init --tools claude --scope global # Non-interactive (global installation)`);
1521
+ console.log(` ${CLI_COMMAND} update # Update configured tools`);
1522
+ console.log(` ${CLI_COMMAND} migrate --from openspec # Migrate from OpenSpec`);
1523
+ console.log(` ${CLI_COMMAND} migrate --from speckit # Migrate from spec-kit`);
1524
+ console.log(` ${CLI_COMMAND} migrate --from openspec --dry-run # Dry run migration`);
1423
1525
  }
1424
1526
 
1425
1527
  // ============================================================================
1426
- // 主入口
1528
+ // Main Entry
1427
1529
  // ============================================================================
1428
1530
 
1429
1531
  async function main() {
1430
1532
  const args = process.argv.slice(2);
1431
1533
 
1432
- // 解析参数
1534
+ // Parse arguments
1433
1535
  let command = null;
1434
1536
  let projectPath = null;
1435
1537
  const options = {};
@@ -1445,6 +1547,8 @@ async function main() {
1445
1547
  process.exit(0);
1446
1548
  } else if (arg === '--tools') {
1447
1549
  options.tools = args[++i];
1550
+ } else if (arg === '--scope') {
1551
+ options.scope = args[++i];
1448
1552
  } else if (arg === '--from') {
1449
1553
  options.from = args[++i];
1450
1554
  } else if (arg === '--dry-run') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev-playbooks",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "AI-powered spec-driven development workflow",
5
5
  "keywords": [
6
6
  "devbooks",
@@ -344,6 +344,54 @@ If you are not using DevBooks, replace `dev-playbooks/specs` / `dev-playbooks/ch
344
344
 
345
345
  ---
346
346
 
347
+ ## `devbooks-test-reviewer` (Test Reviewer) [New]
348
+
349
+ - Purpose: review test quality in `tests/` (coverage, boundary conditions, readability, maintainability); output review comments only; do not modify code.
350
+ - When to use:
351
+ - After Test Owner completes tests, an independent quality review is needed
352
+ - You want to find coverage gaps, missing boundary conditions, and maintainability issues
353
+ - Copy-paste prompt:
354
+ ```text
355
+ You are Test Reviewer. Explicitly use `devbooks-test-reviewer`.
356
+ Review test quality only (coverage/boundary conditions/readability/maintainability); do not modify code.
357
+ Inputs: `tests/**` + `dev-playbooks/changes/<change-id>/verification.md` (if present)
358
+ Output: coverage gaps / missing boundary conditions / maintainability risks / improvement suggestions.
359
+ ```
360
+
361
+ ---
362
+
363
+ ## `devbooks-convergence-audit` (Convergence Auditor) [New]
364
+
365
+ - Purpose: evaluate DevBooks workflow convergence using "evidence-first, distrust declarations" principle; detect "Sisyphus anti-pattern" and "false completion". Actively verify rather than trust document assertions.
366
+ - Anti-confusion design: never trust any assertion in documents (Status: Done, AC checked, etc.); must confirm through verifiable evidence.
367
+ - When to use:
368
+ - You want to evaluate true workflow convergence (not document claims)
369
+ - You suspect a change package is "false completion" or has "stale evidence"
370
+ - You want to detect Sisyphus trap (repeated rework without convergence)
371
+ - Copy-paste prompt:
372
+ ```text
373
+ You are Convergence Auditor. Explicitly use `devbooks-convergence-audit`.
374
+ First read: `dev-playbooks/project.md` (if present)
375
+ Goal: perform a convergence audit on a specified change package.
376
+
377
+ Execute with anti-confusion principles:
378
+ 1) Read all documents under `dev-playbooks/changes/<change-id>/`
379
+ 2) Verify each "declaration" (Status=Done, AC checked, etc.) against evidence
380
+ 3) Actually run tests for verification (if possible)
381
+ 4) Output: declaration vs evidence comparison table + trustworthiness score + confusion detection results
382
+
383
+ Change package: <change-id>
384
+ truth-root: dev-playbooks/specs
385
+ change-root: dev-playbooks/changes
386
+ ```
387
+ - Trustworthiness scoring:
388
+ - 90-100: ✅ Trustworthy convergence, continue current workflow
389
+ - 70-89: ⚠️ Partially trustworthy, needs additional verification
390
+ - 50-69: 🟠 Questionable, parts need rework
391
+ - < 50: 🔴 Untrustworthy (Sisyphus trap), needs full review
392
+
393
+ ---
394
+
347
395
  ## Generating SCIP Index (Manual)
348
396
 
349
397
  If you need graph-based code understanding (call graph, impact analysis, symbol references), you can generate a SCIP index manually:
@@ -28,7 +28,42 @@ Before execution, **must** search for configuration in the following order (stop
28
28
 
29
29
  ## Output Location
30
30
 
31
- - Recommended: Write to the Impact section of `<change-root>/<change-id>/proposal.md` (or create a separate analysis document and backfill later)
31
+ - **Must** write to: The Impact section of `<change-root>/<change-id>/proposal.md`
32
+ - Alternative: Separate `impact-analysis.md` file (to be backfilled to proposal.md later)
33
+
34
+ ## Output Behavior (Critical Constraint)
35
+
36
+ > **Golden Rule**: **Write directly to document, do NOT output to conversation window**
37
+
38
+ ### Must Follow
39
+
40
+ 1. **Direct Write**: Use `Edit` or `Write` tool to write analysis results directly to target document
41
+ 2. **No Echo**: Do NOT display the complete analysis content in the conversation
42
+ 3. **Brief Notification**: After completion, only inform the user "Impact analysis written to `<file-path>`"
43
+
44
+ ### Correct vs Incorrect Behavior
45
+
46
+ | Scenario | ❌ Incorrect Behavior | ✅ Correct Behavior |
47
+ |----------|----------------------|---------------------|
48
+ | Analysis complete | Output full Impact table in conversation | Use Edit tool to write to proposal.md |
49
+ | Notify user | Repeat analysis content | "Impact analysis written to `changes/xxx/proposal.md`" |
50
+ | Large results | Paginate output to conversation | Write all to file, inform file location |
51
+
52
+ ### Example Conversation
53
+
54
+ ```
55
+ User: Analyze the impact of modifying UserService
56
+
57
+ AI: [Uses Grep/CKB to analyze references]
58
+ [Uses Edit tool to write to proposal.md]
59
+
60
+ Impact analysis written to the Impact section of `changes/refactor-user/proposal.md`.
61
+ - Direct impact: 8 files
62
+ - Indirect impact: 12 files
63
+ - Risk level: Medium
64
+
65
+ Open the file to view details.
66
+ ```
32
67
 
33
68
  ## Execution Method
34
69