dev-playbooks 1.5.0 → 1.5.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/bin/devbooks.js +177 -73
- package/package.json +1 -1
- package/skills/devbooks-impact-analysis/SKILL.md +36 -1
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
|
-
//
|
|
594
|
+
// Install Skills (Claude Code, Codex CLI, Qoder)
|
|
546
595
|
// ============================================================================
|
|
547
596
|
|
|
548
|
-
function
|
|
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
|
|
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:
|
|
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: '
|
|
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
|
-
//
|
|
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
|
-
//
|
|
1092
|
+
// Replace existing ai_tools section
|
|
1026
1093
|
configContent = configContent.replace(/ai_tools:[\s\S]*?(?=\n\w|\n$|$)/, toolsYaml + '\n');
|
|
1027
1094
|
} else {
|
|
1028
|
-
//
|
|
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
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
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('
|
|
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('ℹ') + `
|
|
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('
|
|
1177
|
+
// Create project structure
|
|
1178
|
+
const spinner = ora('Creating project structure...').start();
|
|
1089
1179
|
const templateCount = createProjectStructure(projectDir);
|
|
1090
|
-
spinner.succeed(
|
|
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(`
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
//
|
|
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('
|
|
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
|
-
|
|
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 (
|
|
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]
|
|
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>
|
|
1373
|
-
console.log('
|
|
1374
|
-
console.log(' --
|
|
1375
|
-
console.log('
|
|
1376
|
-
console.log(' --
|
|
1377
|
-
console.log(' --
|
|
1378
|
-
console.log(' -
|
|
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('
|
|
1482
|
+
console.log(chalk.cyan('Supported AI Tools:'));
|
|
1382
1483
|
|
|
1383
|
-
//
|
|
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(' ★
|
|
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 #
|
|
1418
|
-
console.log(` ${CLI_COMMAND} init --tools claude,cursor #
|
|
1419
|
-
console.log(` ${CLI_COMMAND}
|
|
1420
|
-
console.log(` ${CLI_COMMAND}
|
|
1421
|
-
console.log(` ${CLI_COMMAND} migrate --from
|
|
1422
|
-
console.log(` ${CLI_COMMAND} migrate --from
|
|
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
|
@@ -28,7 +28,42 @@ Before execution, **must** search for configuration in the following order (stop
|
|
|
28
28
|
|
|
29
29
|
## Output Location
|
|
30
30
|
|
|
31
|
-
-
|
|
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
|
|