@xubill/xx-cli 1.0.6 → 2.0.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.
- package/bin/cli.js +2 -0
- package/lib/core/index.js +164 -17
- package/lib/plugins/config-manager.js +86 -61
- package/lib/plugins/history.js +53 -42
- package/lib/plugins/plugin-manager.js +421 -288
- package/lib/utils/config-manager.js +1 -1
- package/lib/utils/history-manager.js +2 -1
- package/lib/utils/plugins-helper.js +276 -0
- package/package.json +1 -1
- package/readme.md +223 -16
package/bin/cli.js
CHANGED
|
@@ -60,12 +60,14 @@
|
|
|
60
60
|
// 全局错误处理
|
|
61
61
|
process.on('uncaughtException', (error) => {
|
|
62
62
|
console.error(chalk.red('错误:'), error.message);
|
|
63
|
+
console.error(chalk.red('错误堆栈:'), error.stack);
|
|
63
64
|
console.error(chalk.yellow('提示:'), '请检查命令参数是否正确,或使用 --help 查看帮助信息');
|
|
64
65
|
process.exit(1);
|
|
65
66
|
});
|
|
66
67
|
|
|
67
68
|
process.on('unhandledRejection', (error) => {
|
|
68
69
|
console.error(chalk.red('错误:'), error.message);
|
|
70
|
+
console.error(chalk.red('错误堆栈:'), error.stack);
|
|
69
71
|
console.error(chalk.yellow('提示:'), '请检查命令参数是否正确,或使用 --help 查看帮助信息');
|
|
70
72
|
process.exit(1);
|
|
71
73
|
});
|
package/lib/core/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const path = require("path");
|
|
|
8
8
|
const logger = require("../utils/logger");
|
|
9
9
|
const pkg = require("../../package.json");
|
|
10
10
|
const pluginConfig = require("../utils/plugin-config");
|
|
11
|
+
const { createPluginsHelper } = require('../utils/plugins-helper');
|
|
11
12
|
|
|
12
13
|
class Core {
|
|
13
14
|
constructor() {
|
|
@@ -42,7 +43,7 @@ class Core {
|
|
|
42
43
|
const userPluginsDir = path.join(
|
|
43
44
|
process.env.HOME || process.env.USERPROFILE,
|
|
44
45
|
".xx-cli",
|
|
45
|
-
"plugins"
|
|
46
|
+
"plugins",
|
|
46
47
|
);
|
|
47
48
|
|
|
48
49
|
// 确保用户插件目录存在
|
|
@@ -64,10 +65,10 @@ class Core {
|
|
|
64
65
|
// 将用户插件添加到插件文件列表中
|
|
65
66
|
pluginFiles = [...pluginFiles, ...userPluginFiles];
|
|
66
67
|
}
|
|
67
|
-
|
|
68
68
|
// 只收集插件文件信息,不立即加载
|
|
69
69
|
this.pluginFiles = pluginFiles;
|
|
70
70
|
}
|
|
71
|
+
|
|
71
72
|
/**
|
|
72
73
|
* 加载单个插件
|
|
73
74
|
* @param {string} pluginFile - 插件文件名
|
|
@@ -85,7 +86,7 @@ class Core {
|
|
|
85
86
|
const userPluginsDir = path.join(
|
|
86
87
|
process.env.HOME || process.env.USERPROFILE,
|
|
87
88
|
".xx-cli",
|
|
88
|
-
"plugins"
|
|
89
|
+
"plugins",
|
|
89
90
|
);
|
|
90
91
|
|
|
91
92
|
// 优先从内置插件目录加载
|
|
@@ -111,14 +112,14 @@ class Core {
|
|
|
111
112
|
const coreDir = path.join(__dirname, "../core");
|
|
112
113
|
pluginContent = pluginContent.replace(
|
|
113
114
|
/require\('\.\.\/core\//g,
|
|
114
|
-
`require('${coreDir}
|
|
115
|
+
`require('${coreDir}/`,
|
|
115
116
|
);
|
|
116
117
|
|
|
117
118
|
// 替换 ../utils 为绝对路径
|
|
118
119
|
const utilsDir = path.join(__dirname, "../utils");
|
|
119
120
|
pluginContent = pluginContent.replace(
|
|
120
121
|
/require\('\.\.\/utils\//g,
|
|
121
|
-
`require('${utilsDir}
|
|
122
|
+
`require('${utilsDir}/`,
|
|
122
123
|
);
|
|
123
124
|
|
|
124
125
|
// 创建一个临时模块来加载修改后的插件内容
|
|
@@ -132,8 +133,17 @@ class Core {
|
|
|
132
133
|
|
|
133
134
|
const Plugin = tempModule.exports;
|
|
134
135
|
|
|
135
|
-
//
|
|
136
|
-
if (
|
|
136
|
+
// 检查是否为新格式的插件对象
|
|
137
|
+
if (
|
|
138
|
+
Plugin &&
|
|
139
|
+
typeof Plugin === "object" &&
|
|
140
|
+
Plugin.name &&
|
|
141
|
+
(Plugin.action || Plugin.subcommands)
|
|
142
|
+
) {
|
|
143
|
+
// 新格式插件对象,不需要实例化
|
|
144
|
+
this.pluginCache.set(pluginFile, Plugin);
|
|
145
|
+
return Plugin;
|
|
146
|
+
} else if (typeof Plugin === "function" && Plugin.prototype) {
|
|
137
147
|
// 实例化类插件
|
|
138
148
|
const pluginInstance = new Plugin();
|
|
139
149
|
pluginInstance.pluginName =
|
|
@@ -144,16 +154,28 @@ class Core {
|
|
|
144
154
|
return pluginInstance;
|
|
145
155
|
} else {
|
|
146
156
|
// 保持非类插件的兼容性
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
157
|
+
// 对于函数式插件,也可能是新格式
|
|
158
|
+
if (
|
|
159
|
+
Plugin &&
|
|
160
|
+
typeof Plugin === "object" &&
|
|
161
|
+
Plugin.name &&
|
|
162
|
+
(Plugin.action || Plugin.subcommands)
|
|
163
|
+
) {
|
|
164
|
+
// 新格式插件对象
|
|
165
|
+
this.pluginCache.set(pluginFile, Plugin);
|
|
166
|
+
return Plugin;
|
|
167
|
+
} else {
|
|
168
|
+
// 旧格式函数式插件
|
|
169
|
+
this.pluginCache.set(pluginFile, Plugin);
|
|
170
|
+
return Plugin;
|
|
171
|
+
}
|
|
151
172
|
}
|
|
152
173
|
} catch (error) {
|
|
153
174
|
logger.error(`加载插件 ${pluginFile} 失败: ${error.message}`);
|
|
154
175
|
return null;
|
|
155
176
|
}
|
|
156
177
|
}
|
|
178
|
+
|
|
157
179
|
/**
|
|
158
180
|
* 获取所有插件信息
|
|
159
181
|
* @returns {Array} 插件信息数组
|
|
@@ -187,15 +209,29 @@ class Core {
|
|
|
187
209
|
}
|
|
188
210
|
|
|
189
211
|
const plugin = this.loadPlugin(pluginFile);
|
|
190
|
-
if (plugin
|
|
212
|
+
if (plugin) {
|
|
191
213
|
try {
|
|
192
|
-
|
|
214
|
+
// 检查是否为新格式插件
|
|
215
|
+
if (
|
|
216
|
+
plugin.name &&
|
|
217
|
+
(typeof plugin.action === "function" ||
|
|
218
|
+
(plugin.subcommands && Array.isArray(plugin.subcommands)))
|
|
219
|
+
) {
|
|
220
|
+
this.registerNewFormatPlugin(program, plugin, pluginName);
|
|
221
|
+
} else if (plugin.registerCommands) {
|
|
222
|
+
// 旧格式插件
|
|
223
|
+
plugin.registerCommands(program);
|
|
224
|
+
}
|
|
193
225
|
} catch (error) {
|
|
194
226
|
// 捕获命令别名冲突等错误,确保其他插件能够正常注册
|
|
195
|
-
if (error.message && error.message.includes(
|
|
196
|
-
console.warn(
|
|
227
|
+
if (error.message && error.message.includes("cannot add alias")) {
|
|
228
|
+
console.warn(
|
|
229
|
+
`警告: 插件 ${pluginName} 的命令别名与其他插件冲突,已跳过此插件的命令注册`,
|
|
230
|
+
);
|
|
197
231
|
} else {
|
|
198
|
-
console.error(
|
|
232
|
+
console.error(
|
|
233
|
+
`错误: 插件 ${pluginName} 注册命令失败: ${error.message}`,
|
|
234
|
+
);
|
|
199
235
|
}
|
|
200
236
|
}
|
|
201
237
|
}
|
|
@@ -206,6 +242,117 @@ class Core {
|
|
|
206
242
|
this.registerCommandSuggestions(program);
|
|
207
243
|
}
|
|
208
244
|
|
|
245
|
+
/**
|
|
246
|
+
* 注册新格式插件命令
|
|
247
|
+
* @param {Object} program - Commander 实例
|
|
248
|
+
* @param {Object} plugin - 插件对象
|
|
249
|
+
* @param {string} pluginName - 插件名称
|
|
250
|
+
*/
|
|
251
|
+
registerNewFormatPlugin(program, plugin, pluginName) {
|
|
252
|
+
const cmd = program.command(plugin.name);
|
|
253
|
+
|
|
254
|
+
// 设置描述
|
|
255
|
+
if (plugin.description) {
|
|
256
|
+
cmd.description(plugin.description);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// 设置别名
|
|
260
|
+
if (plugin.alias) {
|
|
261
|
+
cmd.alias(plugin.alias);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// 添加选项
|
|
265
|
+
if (plugin.options && Array.isArray(plugin.options)) {
|
|
266
|
+
plugin.options.forEach((option) => {
|
|
267
|
+
if (option.name && option.description) {
|
|
268
|
+
cmd.option(option.name, option.description);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 如果插件定义了子命令,则注册它们
|
|
274
|
+
if (plugin.subcommands && Array.isArray(plugin.subcommands)) {
|
|
275
|
+
plugin.subcommands.forEach((subcmd) => {
|
|
276
|
+
const subCommand = cmd.command(subcmd.name);
|
|
277
|
+
|
|
278
|
+
if (subcmd.description) {
|
|
279
|
+
subCommand.description(subcmd.description);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (subcmd.options && Array.isArray(subcmd.options)) {
|
|
283
|
+
subcmd.options.forEach((option) => {
|
|
284
|
+
if (option.name && option.description) {
|
|
285
|
+
subCommand.option(option.name, option.description);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
subCommand.action(async (...args) => {
|
|
291
|
+
let commandArgs = [];
|
|
292
|
+
let options = {};
|
|
293
|
+
|
|
294
|
+
if (args.length > 0) {
|
|
295
|
+
const lastArg = args[args.length - 1];
|
|
296
|
+
if (lastArg && typeof lastArg === 'object' && typeof lastArg.opts === 'function') {
|
|
297
|
+
options = { ...lastArg.opts() };
|
|
298
|
+
commandArgs = lastArg.args || [];
|
|
299
|
+
} else {
|
|
300
|
+
commandArgs = [...args];
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// 创建插件帮助器实例
|
|
305
|
+
const helper = createPluginsHelper(plugin.name);
|
|
306
|
+
// 调用子命令的 action 方法
|
|
307
|
+
await subcmd.action(commandArgs, options, helper);
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// 设置主命令的 action
|
|
313
|
+
if (plugin.action) {
|
|
314
|
+
cmd.action(async (...args) => {
|
|
315
|
+
// Commander.js 会将参数和选项作为单独的参数传递
|
|
316
|
+
// 最后一个参数通常是 Command 对象,其中包含选项
|
|
317
|
+
let commandArgs = [];
|
|
318
|
+
let options = {};
|
|
319
|
+
|
|
320
|
+
// 如果有参数,需要正确识别哪个是 Command 对象
|
|
321
|
+
if (args.length > 0) {
|
|
322
|
+
// 从后往前检查,找到 Command 对象
|
|
323
|
+
let commandObjIndex = -1;
|
|
324
|
+
for (let i = args.length - 1; i >= 0; i--) {
|
|
325
|
+
const arg = args[i];
|
|
326
|
+
if (
|
|
327
|
+
arg &&
|
|
328
|
+
typeof arg === "object" &&
|
|
329
|
+
typeof arg.opts === "function"
|
|
330
|
+
) {
|
|
331
|
+
commandObjIndex = i;
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (commandObjIndex !== -1) {
|
|
337
|
+
// 找到了 Command 对象
|
|
338
|
+
options = { ...args[commandObjIndex].opts() };
|
|
339
|
+
// 从 Command 对象的 args 属性获取命令参数
|
|
340
|
+
const commandObj = args[commandObjIndex];
|
|
341
|
+
commandArgs = commandObj.args || [];
|
|
342
|
+
} else {
|
|
343
|
+
// 没有 Command 对象,所有参数都是命令参数
|
|
344
|
+
commandArgs = [...args];
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// 创建插件帮助器实例
|
|
349
|
+
const helper = createPluginsHelper(plugin.name);
|
|
350
|
+
// 调用插件的 action 方法
|
|
351
|
+
await plugin.action(commandArgs, options, helper);
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
209
356
|
/**
|
|
210
357
|
* 注册命令自动建议功能
|
|
211
358
|
* @param {Object} program - Commander 实例
|
|
@@ -322,7 +469,7 @@ class Core {
|
|
|
322
469
|
} catch (error) {
|
|
323
470
|
console.error(
|
|
324
471
|
`执行插件 ${pluginName} 的钩子 ${event} 时出错:`,
|
|
325
|
-
error.message
|
|
472
|
+
error.message,
|
|
326
473
|
);
|
|
327
474
|
}
|
|
328
475
|
}
|
|
@@ -21,120 +21,145 @@
|
|
|
21
21
|
|
|
22
22
|
const fs = require('fs-extra');
|
|
23
23
|
const path = require('path');
|
|
24
|
-
const BasePlugin = require('../core/base-plugin');
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
*/
|
|
36
|
-
registerCommands(program) {
|
|
37
|
-
// 配置管理命令
|
|
38
|
-
const configCommand = program.command('config-manager')
|
|
39
|
-
.alias('conf')
|
|
40
|
-
.description('管理所有插件的配置文件');
|
|
41
|
-
|
|
42
|
-
// 导出配置命令
|
|
43
|
-
configCommand.command('export [target]')
|
|
44
|
-
.description('导出所有插件配置到指定目录,默认导出到当前目录的 config-backup 文件夹')
|
|
45
|
-
.action((target) => this.exportConfig(target));
|
|
46
|
-
|
|
47
|
-
// 导入配置命令
|
|
48
|
-
configCommand.command('import [source]')
|
|
49
|
-
.description('从指定目录导入配置并覆盖,默认从当前目录的 config-backup 文件夹导入')
|
|
50
|
-
.action((source) => this.importConfig(source));
|
|
51
|
-
|
|
52
|
-
// 列出配置命令
|
|
53
|
-
configCommand.command('list')
|
|
54
|
-
.description('列出所有插件的配置文件位置和状态')
|
|
55
|
-
.action(() => this.listConfig());
|
|
25
|
+
/**
|
|
26
|
+
* 配置管理类
|
|
27
|
+
* 封装所有配置管理相关的方法
|
|
28
|
+
*/
|
|
29
|
+
class ConfigManager {
|
|
30
|
+
constructor() {
|
|
31
|
+
// 获取用户主目录,添加 process.cwd() 作为 fallback
|
|
32
|
+
this.homeDir = process.env.HOME || process.env.USERPROFILE || process.cwd();
|
|
33
|
+
this.xxDir = path.join(this.homeDir, '.xx-cli');
|
|
56
34
|
}
|
|
57
35
|
|
|
58
36
|
/**
|
|
59
37
|
* 导出配置
|
|
60
38
|
* @param {string} target - 目标路径
|
|
39
|
+
* @param {Object} helper - 插件帮助器
|
|
61
40
|
*/
|
|
62
|
-
exportConfig(target) {
|
|
63
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
64
|
-
const xxDir = path.join(homeDir, '.xx-cli');
|
|
41
|
+
exportConfig(target, helper) {
|
|
65
42
|
const exportDir = target || path.join(process.cwd(), 'config-backup');
|
|
66
|
-
|
|
43
|
+
helper.showInfo(`开始导出配置到: ${exportDir}`);
|
|
67
44
|
|
|
68
45
|
// 确保导出目录存在
|
|
69
46
|
fs.ensureDirSync(exportDir);
|
|
70
47
|
|
|
71
48
|
// 复制 .xx 目录
|
|
72
|
-
if (fs.existsSync(xxDir)) {
|
|
49
|
+
if (fs.existsSync(this.xxDir)) {
|
|
73
50
|
const exportPath = path.join(exportDir, '.xx-cli');
|
|
74
51
|
fs.ensureDirSync(path.dirname(exportPath));
|
|
75
|
-
fs.copySync(xxDir, exportPath);
|
|
76
|
-
|
|
52
|
+
fs.copySync(this.xxDir, exportPath);
|
|
53
|
+
helper.showInfo(`导出目录: ${this.xxDir}`);
|
|
77
54
|
}
|
|
78
55
|
|
|
79
|
-
|
|
56
|
+
helper.showSuccess('配置导出完成!');
|
|
80
57
|
}
|
|
81
58
|
|
|
82
59
|
/**
|
|
83
60
|
* 导入配置
|
|
84
61
|
* @param {string} source - 源路径
|
|
62
|
+
* @param {Object} helper - 插件帮助器
|
|
85
63
|
*/
|
|
86
|
-
importConfig(source) {
|
|
87
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
88
|
-
const xxDir = path.join(homeDir, '.xx-cli');
|
|
64
|
+
importConfig(source, helper) {
|
|
89
65
|
const importDir = source || path.join(process.cwd(), 'config-backup');
|
|
90
|
-
|
|
66
|
+
helper.showInfo(`开始从 ${importDir} 导入配置...`);
|
|
91
67
|
|
|
92
68
|
// 检查导入目录是否存在
|
|
93
69
|
if (!fs.existsSync(importDir)) {
|
|
94
|
-
|
|
70
|
+
helper.showError(`导入目录不存在: ${importDir}`);
|
|
95
71
|
return;
|
|
96
72
|
}
|
|
97
73
|
|
|
98
74
|
// 复制配置到 .xx 目录
|
|
99
75
|
const importPath = path.join(importDir, '.xx-cli');
|
|
100
76
|
if (fs.existsSync(importPath)) {
|
|
101
|
-
fs.ensureDirSync(path.dirname(xxDir));
|
|
102
|
-
fs.copySync(importPath, xxDir, { overwrite: true });
|
|
103
|
-
|
|
77
|
+
fs.ensureDirSync(path.dirname(this.xxDir));
|
|
78
|
+
fs.copySync(importPath, this.xxDir, { overwrite: true });
|
|
79
|
+
helper.showInfo(`导入目录: ${importPath}`);
|
|
104
80
|
}
|
|
105
81
|
|
|
106
|
-
|
|
82
|
+
helper.showSuccess('配置导入完成!');
|
|
107
83
|
}
|
|
108
84
|
|
|
109
85
|
/**
|
|
110
86
|
* 列出配置
|
|
87
|
+
* @param {Object} helper - 插件帮助器
|
|
111
88
|
*/
|
|
112
|
-
listConfig() {
|
|
113
|
-
|
|
114
|
-
const xxDir = path.join(homeDir, '.xx-cli');
|
|
115
|
-
console.log('列出所有插件配置文件...\n');
|
|
89
|
+
listConfig(helper) {
|
|
90
|
+
helper.showInfo('列出所有插件配置文件...\n');
|
|
116
91
|
|
|
117
|
-
if (fs.existsSync(xxDir)) {
|
|
118
|
-
const items = fs.readdirSync(xxDir).filter(item => !item.startsWith('.') && item !== 'plugins');
|
|
92
|
+
if (fs.existsSync(this.xxDir)) {
|
|
93
|
+
const items = fs.readdirSync(this.xxDir).filter(item => !item.startsWith('.') && item !== 'plugins');
|
|
119
94
|
items.forEach(item => {
|
|
120
|
-
const itemPath = path.join(xxDir, item);
|
|
95
|
+
const itemPath = path.join(this.xxDir, item);
|
|
121
96
|
const stat = fs.statSync(itemPath);
|
|
122
97
|
if (stat.isDirectory()) {
|
|
123
|
-
|
|
98
|
+
helper.showInfo(`插件配置目录: ${itemPath}`);
|
|
124
99
|
const configFiles = fs.readdirSync(itemPath).filter(file => !file.startsWith('.'));
|
|
125
100
|
configFiles.forEach(file => {
|
|
126
|
-
|
|
101
|
+
helper.showInfo(` - ${file}`);
|
|
127
102
|
});
|
|
128
103
|
} else if (stat.isFile()) {
|
|
129
|
-
|
|
104
|
+
helper.showInfo(`配置文件: ${itemPath}`);
|
|
130
105
|
}
|
|
131
106
|
});
|
|
132
107
|
} else {
|
|
133
|
-
|
|
108
|
+
helper.showWarning('配置目录不存在');
|
|
134
109
|
}
|
|
135
110
|
|
|
136
|
-
|
|
111
|
+
helper.showSuccess('配置列表完成!');
|
|
137
112
|
}
|
|
138
113
|
}
|
|
139
114
|
|
|
140
|
-
|
|
115
|
+
// 创建配置管理实例
|
|
116
|
+
const configManager = new ConfigManager();
|
|
117
|
+
|
|
118
|
+
module.exports = {
|
|
119
|
+
name: 'config-manager',
|
|
120
|
+
alias: 'conf',
|
|
121
|
+
description: '管理所有插件的配置文件',
|
|
122
|
+
subcommands: [
|
|
123
|
+
{
|
|
124
|
+
name: 'export',
|
|
125
|
+
description: '导出所有插件配置到指定目录,默认导出到当前目录的 config-backup 文件夹',
|
|
126
|
+
action: (args, options, helper) => {
|
|
127
|
+
configManager.exportConfig(args.target, helper);
|
|
128
|
+
},
|
|
129
|
+
args: [
|
|
130
|
+
{
|
|
131
|
+
name: 'target',
|
|
132
|
+
description: '目标路径',
|
|
133
|
+
optional: true
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: 'import',
|
|
139
|
+
description: '从指定目录导入配置并覆盖,默认从当前目录的 config-backup 文件夹导入',
|
|
140
|
+
action: (args, options, helper) => {
|
|
141
|
+
configManager.importConfig(args.source, helper);
|
|
142
|
+
},
|
|
143
|
+
args: [
|
|
144
|
+
{
|
|
145
|
+
name: 'source',
|
|
146
|
+
description: '源路径',
|
|
147
|
+
optional: true
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'list',
|
|
153
|
+
description: '列出所有插件的配置文件位置和状态',
|
|
154
|
+
action: (args, options, helper) => {
|
|
155
|
+
configManager.listConfig(helper);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
],
|
|
159
|
+
options: [
|
|
160
|
+
{
|
|
161
|
+
name: '-h, --help',
|
|
162
|
+
description: '显示帮助信息'
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
};
|
package/lib/plugins/history.js
CHANGED
|
@@ -21,77 +21,88 @@
|
|
|
21
21
|
* - 支持命令行参数
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* 注册命令
|
|
34
|
-
* @param {Object} program - Commander.js 实例
|
|
35
|
-
*/
|
|
36
|
-
registerCommands(program) {
|
|
37
|
-
// 命令历史记录
|
|
38
|
-
program
|
|
39
|
-
.command('history')
|
|
40
|
-
.alias('h')
|
|
41
|
-
.description('查看命令历史记录')
|
|
42
|
-
.option('-l, --limit <number>', '限制显示的历史记录数量', parseInt, 10)
|
|
43
|
-
.option('-c, --clear', '清除命令历史记录')
|
|
44
|
-
.option('-s, --search <keyword>', '搜索命令历史记录')
|
|
45
|
-
.action((options) => this.showHistory(options));
|
|
24
|
+
/**
|
|
25
|
+
* 历史记录管理类
|
|
26
|
+
* 封装所有历史记录相关的方法
|
|
27
|
+
*/
|
|
28
|
+
class HistoryManager {
|
|
29
|
+
constructor() {
|
|
30
|
+
// 加载历史记录管理器
|
|
31
|
+
this.historyManager = require('../utils/history-manager');
|
|
46
32
|
}
|
|
47
33
|
|
|
48
34
|
/**
|
|
49
35
|
* 显示命令历史记录
|
|
50
36
|
* @param {Object} options - 选项
|
|
37
|
+
* @param {Object} helper - 插件帮助器
|
|
51
38
|
*/
|
|
52
|
-
showHistory(options) {
|
|
53
|
-
const historyManager = require('../utils/history-manager');
|
|
54
|
-
|
|
39
|
+
showHistory(options, helper) {
|
|
55
40
|
if (options.clear) {
|
|
56
41
|
// 清除命令历史记录
|
|
57
|
-
historyManager.clearHistory();
|
|
58
|
-
|
|
42
|
+
this.historyManager.clearHistory();
|
|
43
|
+
helper.showSuccess('命令历史记录已清除');
|
|
59
44
|
return;
|
|
60
45
|
}
|
|
61
46
|
|
|
62
47
|
if (options.search) {
|
|
63
48
|
// 搜索命令历史记录
|
|
64
|
-
const searchResults = historyManager.searchHistory(options.search);
|
|
49
|
+
const searchResults = this.historyManager.searchHistory(options.search);
|
|
65
50
|
if (searchResults.length > 0) {
|
|
66
|
-
|
|
67
|
-
|
|
51
|
+
helper.showInfo('\n搜索结果:');
|
|
52
|
+
helper.showInfo('=====================================');
|
|
68
53
|
searchResults.forEach((item, index) => {
|
|
69
54
|
const date = new Date(item.timestamp).toLocaleString();
|
|
70
|
-
|
|
55
|
+
helper.showInfo(`${index + 1}. ${item.command} (${date})`);
|
|
71
56
|
});
|
|
72
|
-
|
|
73
|
-
|
|
57
|
+
helper.showInfo('=====================================');
|
|
58
|
+
helper.showInfo(`总计: ${searchResults.length} 条记录\n`);
|
|
74
59
|
} else {
|
|
75
|
-
|
|
60
|
+
helper.showInfo(`\n未找到匹配 "${options.search}" 的命令历史记录\n`);
|
|
76
61
|
}
|
|
77
62
|
return;
|
|
78
63
|
}
|
|
79
64
|
|
|
80
65
|
// 显示命令历史记录
|
|
81
|
-
const history = historyManager.getHistory(options.limit);
|
|
66
|
+
const history = this.historyManager.getHistory(options.limit);
|
|
82
67
|
if (history.length > 0) {
|
|
83
|
-
|
|
84
|
-
|
|
68
|
+
helper.showInfo('\n命令历史记录:');
|
|
69
|
+
helper.showInfo('=====================================');
|
|
85
70
|
history.forEach((item, index) => {
|
|
86
71
|
const date = new Date(item.timestamp).toLocaleString();
|
|
87
|
-
|
|
72
|
+
helper.showInfo(`${index + 1}. ${item.command} (${date})`);
|
|
88
73
|
});
|
|
89
|
-
|
|
90
|
-
|
|
74
|
+
helper.showInfo('=====================================');
|
|
75
|
+
helper.showInfo(`总计: ${history.length} 条记录\n`);
|
|
91
76
|
} else {
|
|
92
|
-
|
|
77
|
+
helper.showInfo('\n命令历史记录为空\n');
|
|
93
78
|
}
|
|
94
79
|
}
|
|
95
80
|
}
|
|
96
81
|
|
|
97
|
-
|
|
82
|
+
// 创建历史记录管理实例
|
|
83
|
+
const historyManager = new HistoryManager();
|
|
84
|
+
|
|
85
|
+
module.exports = {
|
|
86
|
+
name: 'history',
|
|
87
|
+
alias: 'h',
|
|
88
|
+
description: '查看命令历史记录',
|
|
89
|
+
options: [
|
|
90
|
+
{
|
|
91
|
+
name: '-l, --limit <number>',
|
|
92
|
+
description: '限制显示的历史记录数量',
|
|
93
|
+
parse: parseInt,
|
|
94
|
+
default: 10
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: '-c, --clear',
|
|
98
|
+
description: '清除命令历史记录'
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: '-s, --search <keyword>',
|
|
102
|
+
description: '搜索命令历史记录'
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
action: (args, options, helper) => {
|
|
106
|
+
historyManager.showHistory(options, helper);
|
|
107
|
+
}
|
|
108
|
+
};
|