@xubill/xx-cli 1.0.2 → 1.0.4

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.
@@ -0,0 +1,97 @@
1
+ /**
2
+ * 历史记录插件
3
+ * 用于查看命令历史记录
4
+ *
5
+ * 命令说明:
6
+ * - history [options]:查看命令历史记录
7
+ * - 示例:history
8
+ * - 示例:history --limit 20
9
+ * - 示例:history --clear
10
+ * - 示例:history --search <keyword>
11
+ *
12
+ * 功能说明:
13
+ * - 查看命令历史记录
14
+ * - 限制显示的历史记录数量
15
+ * - 清除命令历史记录
16
+ * - 搜索命令历史记录
17
+ *
18
+ * 用户体验:
19
+ * - 使用标准化的用户提示
20
+ * - 提供清晰的错误提示
21
+ * - 支持命令行参数
22
+ */
23
+
24
+ const BasePlugin = require('../core/base-plugin');
25
+
26
+ class HistoryPlugin extends BasePlugin {
27
+ constructor(options = {}) {
28
+ super(options);
29
+ this.pluginName = 'history';
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));
46
+ }
47
+
48
+ /**
49
+ * 显示命令历史记录
50
+ * @param {Object} options - 选项
51
+ */
52
+ showHistory(options) {
53
+ const historyManager = require('../utils/history-manager');
54
+
55
+ if (options.clear) {
56
+ // 清除命令历史记录
57
+ historyManager.clearHistory();
58
+ console.log('命令历史记录已清除');
59
+ return;
60
+ }
61
+
62
+ if (options.search) {
63
+ // 搜索命令历史记录
64
+ const searchResults = historyManager.searchHistory(options.search);
65
+ if (searchResults.length > 0) {
66
+ console.log('\n搜索结果:');
67
+ console.log('=====================================');
68
+ searchResults.forEach((item, index) => {
69
+ const date = new Date(item.timestamp).toLocaleString();
70
+ console.log(`${index + 1}. ${item.command} (${date})`);
71
+ });
72
+ console.log('=====================================');
73
+ console.log(`总计: ${searchResults.length} 条记录\n`);
74
+ } else {
75
+ console.log(`\n未找到匹配 "${options.search}" 的命令历史记录\n`);
76
+ }
77
+ return;
78
+ }
79
+
80
+ // 显示命令历史记录
81
+ const history = historyManager.getHistory(options.limit);
82
+ if (history.length > 0) {
83
+ console.log('\n命令历史记录:');
84
+ console.log('=====================================');
85
+ history.forEach((item, index) => {
86
+ const date = new Date(item.timestamp).toLocaleString();
87
+ console.log(`${index + 1}. ${item.command} (${date})`);
88
+ });
89
+ console.log('=====================================');
90
+ console.log(`总计: ${history.length} 条记录\n`);
91
+ } else {
92
+ console.log('\n命令历史记录为空\n');
93
+ }
94
+ }
95
+ }
96
+
97
+ module.exports = HistoryPlugin;
@@ -1,14 +1,194 @@
1
1
  /**
2
- * 核心命令实现
3
- * 存放具体的命令实现逻辑,保持 index.js 清晰
2
+ * 插件管理插件
3
+ * 用于管理所有插件
4
+ *
5
+ * 命令说明:
6
+ * - plugin [options]:管理所有插件
7
+ * - 示例:plugin list
8
+ * - 示例:plugin enable <plugin>
9
+ * - 示例:plugin disable <plugin>
10
+ * - 示例:plugin add <url>
11
+ * - 示例:plugin remove <plugin>
12
+ * - 示例:plugin create <name>
13
+ * - 示例:plugin config <plugin> [key] [value]
14
+ *
15
+ * 功能说明:
16
+ * - 列出所有插件
17
+ * - 启用指定插件
18
+ * - 禁用指定插件
19
+ * - 添加外部插件
20
+ * - 删除指定插件
21
+ * - 创建指定名称的插件模板
22
+ * - 查看和设置插件配置
23
+ *
24
+ * 用户体验:
25
+ * - 使用标准化的用户提示
26
+ * - 提供清晰的错误提示
27
+ * - 支持命令行参数
4
28
  */
5
29
 
6
30
  const fs = require('fs-extra');
7
31
  const path = require('path');
32
+ const https = require('https');
33
+ const http = require('http');
34
+ const BasePlugin = require('../core/base-plugin');
8
35
  const validator = require('../utils/validator');
9
36
  const pluginConfig = require('../utils/plugin-config');
10
37
 
11
- class CoreCommands {
38
+ class PluginManagerPlugin extends BasePlugin {
39
+ constructor(options = {}) {
40
+ super(options);
41
+ this.pluginName = 'plugin-manager';
42
+ }
43
+
44
+ /**
45
+ * 注册命令
46
+ * @param {Object} program - Commander.js 实例
47
+ */
48
+ registerCommands(program) {
49
+ // 插件管理命令
50
+ const pluginCommand = program.command('plugin')
51
+ .alias('p')
52
+ .description('插件管理');
53
+
54
+ // 列出插件
55
+ pluginCommand.command('list')
56
+ .alias('ls')
57
+ .description('列出所有插件')
58
+ .action(() => this.listPlugins());
59
+
60
+ // 启用插件
61
+ pluginCommand.command('enable <plugin>')
62
+ .alias('en')
63
+ .description('启用插件')
64
+ .action((plugin) => this.enablePlugin(plugin));
65
+
66
+ // 禁用插件
67
+ pluginCommand.command('disable <plugin>')
68
+ .alias('dis')
69
+ .description('禁用插件')
70
+ .action((plugin) => this.disablePlugin(plugin));
71
+
72
+ // 添加外部自定义插件
73
+ pluginCommand.command('add <url>')
74
+ .description('添加外部自定义插件')
75
+ .action((url) => this.addExternalPlugin(url));
76
+
77
+ // 删除插件
78
+ pluginCommand.command('remove <plugin>')
79
+ .alias('rm')
80
+ .description('删除指定插件')
81
+ .action((plugin) => this.removePlugin(plugin));
82
+
83
+ // 创建插件
84
+ pluginCommand.command('create <name>')
85
+ .alias('cr')
86
+ .description('创建指定名称的插件模板')
87
+ .action(async (name, options) => this.createPlugin(name, options));
88
+
89
+ // 插件配置管理
90
+ pluginCommand.command('config <plugin> [key] [value]')
91
+ .alias('c')
92
+ .description('查看和设置插件配置')
93
+ .action((plugin, key, value) => this.managePluginConfig(plugin, key, value));
94
+ }
95
+
96
+ /**
97
+ * 列出所有插件
98
+ */
99
+ listPlugins() {
100
+ const core = require('../core');
101
+ const pluginsInfo = core.getPluginsInfo();
102
+ console.log('\n可用插件列表:');
103
+ console.log('=====================================');
104
+ pluginsInfo.forEach(plugin => {
105
+ const isDisabled = pluginConfig.isPluginDisabled(plugin.name);
106
+ const status = isDisabled ? '[已禁用]' : '[已启用]';
107
+ console.log(`- ${plugin.name} ${status}`);
108
+ });
109
+ console.log('=====================================');
110
+ console.log(`总计: ${pluginsInfo.length} 个插件\n`);
111
+ }
112
+
113
+ /**
114
+ * 启用插件
115
+ * @param {string} pluginName - 插件名称
116
+ */
117
+ enablePlugin(pluginName) {
118
+ // 验证插件名称
119
+ if (!validator.validateCommandParam(pluginName)) {
120
+ console.error('错误: 插件名称包含无效字符');
121
+ return;
122
+ }
123
+
124
+ // 启用插件
125
+ pluginConfig.enablePlugin(pluginName);
126
+ console.log(`已启用插件: ${pluginName}`);
127
+ console.log('提示: 请重新运行命令以应用更改');
128
+ }
129
+
130
+ /**
131
+ * 禁用插件
132
+ * @param {string} pluginName - 插件名称
133
+ */
134
+ disablePlugin(pluginName) {
135
+ // 验证插件名称
136
+ if (!validator.validateCommandParam(pluginName)) {
137
+ console.error('错误: 插件名称包含无效字符');
138
+ return;
139
+ }
140
+
141
+ // 禁用插件
142
+ pluginConfig.disablePlugin(pluginName);
143
+ console.log(`已禁用插件: ${pluginName}`);
144
+ console.log('提示: 请重新运行命令以应用更改');
145
+ }
146
+
147
+ /**
148
+ * 删除插件
149
+ * @param {string} pluginName - 插件名称
150
+ */
151
+ removePlugin(pluginName) {
152
+ // 验证插件名称
153
+ if (!validator.validateCommandParam(pluginName)) {
154
+ console.error('错误: 插件名称包含无效字符');
155
+ return;
156
+ }
157
+
158
+ // 内置插件目录
159
+ const builtinPluginsDir = path.join(__dirname, '../plugins');
160
+ // 用户插件目录
161
+ const userPluginsDir = path.join(process.env.HOME || process.env.USERPROFILE, '.xx-cli', 'plugins');
162
+
163
+ // 构建插件文件路径
164
+ const pluginFileName = `${pluginName}.js`;
165
+ let pluginPath = path.join(builtinPluginsDir, pluginFileName);
166
+
167
+ // 如果内置插件目录中不存在,则从用户插件目录查找
168
+ if (!fs.existsSync(pluginPath)) {
169
+ pluginPath = path.join(userPluginsDir, pluginFileName);
170
+ }
171
+
172
+ // 检查插件文件是否存在
173
+ if (!fs.existsSync(pluginPath)) {
174
+ console.error(`错误: 插件文件不存在`);
175
+ return;
176
+ }
177
+
178
+ try {
179
+ // 删除插件文件
180
+ fs.unlinkSync(pluginPath);
181
+
182
+ // 从配置中移除插件的禁用状态
183
+ pluginConfig.removePluginFromConfig(pluginName);
184
+
185
+ console.log(`已成功删除插件: ${pluginName}`);
186
+ console.log('提示: 请重新运行命令以应用更改');
187
+ } catch (error) {
188
+ console.error(`删除插件失败: ${error.message}`);
189
+ }
190
+ }
191
+
12
192
  /**
13
193
  * 创建插件
14
194
  * @param {string} name - 插件名称
@@ -18,8 +198,8 @@ class CoreCommands {
18
198
  try {
19
199
  console.log('创建插件模板...');
20
200
 
21
- // 插件目录路径
22
- const pluginsDir = path.join(__dirname, '../plugins');
201
+ // 插件目录路径(用户插件目录,不会被重新安装影响)
202
+ const pluginsDir = path.join(process.env.HOME || process.env.USERPROFILE, '.xx-cli', 'plugins');
23
203
  const pluginFileName = `${name}.js`;
24
204
  const pluginPath = path.join(pluginsDir, pluginFileName);
25
205
 
@@ -39,6 +219,32 @@ class CoreCommands {
39
219
  console.log('提示:请编辑插件文件,实现具体功能');
40
220
  console.log('测试方法:运行 "node bin/cli.js <插件命令>"');
41
221
 
222
+ // 使用 vscode 打开创建的插件文件
223
+ try {
224
+ const { exec } = require('child_process');
225
+ const os = require('os');
226
+ let command;
227
+
228
+ // 根据操作系统类型构建不同的命令
229
+ if (os.platform() === 'win32') {
230
+ // Windows 系统
231
+ command = `start code "${pluginPath}"`;
232
+ } else {
233
+ // macOS 和 Linux 系统
234
+ command = `code "${pluginPath}"`;
235
+ }
236
+
237
+ exec(command, (error) => {
238
+ if (error) {
239
+ console.log('提示:无法自动打开 vscode,请手动打开插件文件进行编辑');
240
+ } else {
241
+ console.log('已自动打开 vscode 编辑插件文件');
242
+ }
243
+ });
244
+ } catch (error) {
245
+ console.log('提示:无法自动打开 vscode,请手动打开插件文件进行编辑');
246
+ }
247
+
42
248
  } catch (error) {
43
249
  console.error('创建插件模板失败:', error.message);
44
250
  }
@@ -120,170 +326,6 @@ module.exports = ${this.toCamelCase(name)}Plugin;
120
326
  return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase()).replace(/^[a-z]/, (g) => g.toUpperCase());
121
327
  }
122
328
 
123
- /**
124
- * 配置管理
125
- * @param {string} action - 操作类型
126
- * @param {string} target - 目标路径
127
- */
128
- configManager(action, target) {
129
- const homeDir = process.env.HOME || process.env.USERPROFILE;
130
- const xxDir = path.join(homeDir, '.xx-cli');
131
-
132
- if (action === 'export') {
133
- // 导出配置
134
- const exportDir = target || path.join(process.cwd(), 'config-backup');
135
- console.log(`开始导出配置到: ${exportDir}`);
136
-
137
- // 确保导出目录存在
138
- fs.ensureDirSync(exportDir);
139
-
140
- // 复制 .xx 目录
141
- if (fs.existsSync(xxDir)) {
142
- const exportPath = path.join(exportDir, '.xx-cli');
143
- fs.ensureDirSync(path.dirname(exportPath));
144
- fs.copySync(xxDir, exportPath);
145
- console.log(`导出目录: ${xxDir}`);
146
- }
147
-
148
- console.log('配置导出完成!');
149
- } else if (action === 'import') {
150
- // 导入配置
151
- const importDir = target || path.join(process.cwd(), 'config-backup');
152
- console.log(`开始从 ${importDir} 导入配置...`);
153
-
154
- // 检查导入目录是否存在
155
- if (!fs.existsSync(importDir)) {
156
- console.log(`导入目录不存在: ${importDir}`);
157
- return;
158
- }
159
-
160
- // 复制配置到 .xx 目录
161
- const importPath = path.join(importDir, '.xx-cli');
162
- if (fs.existsSync(importPath)) {
163
- fs.ensureDirSync(path.dirname(xxDir));
164
- fs.copySync(importPath, xxDir, { overwrite: true });
165
- console.log(`导入目录: ${importPath}`);
166
- }
167
-
168
- console.log('配置导入完成!');
169
- } else if (action === 'list') {
170
- // 列出配置
171
- console.log('列出所有插件配置文件...\n');
172
-
173
- if (fs.existsSync(xxDir)) {
174
- const items = fs.readdirSync(xxDir).filter(item => !item.startsWith('.'));
175
- items.forEach(item => {
176
- const itemPath = path.join(xxDir, item);
177
- const stat = fs.statSync(itemPath);
178
- if (stat.isDirectory()) {
179
- console.log(`插件配置目录: ${itemPath}`);
180
- const configFiles = fs.readdirSync(itemPath).filter(file => !file.startsWith('.'));
181
- configFiles.forEach(file => {
182
- console.log(` - ${file}`);
183
- });
184
- } else if (stat.isFile()) {
185
- console.log(`配置文件: ${itemPath}`);
186
- }
187
- });
188
- } else {
189
- console.log('配置目录不存在');
190
- }
191
-
192
- console.log('配置列表完成!');
193
- }
194
- }
195
-
196
- /**
197
- * 列出所有插件
198
- */
199
- listPlugins(core) {
200
- const pluginsInfo = core.getPluginsInfo();
201
- console.log('\n可用插件列表:');
202
- console.log('=====================================');
203
- pluginsInfo.forEach(plugin => {
204
- const isDisabled = pluginConfig.isPluginDisabled(plugin.name);
205
- const status = isDisabled ? '[已禁用]' : '[已启用]';
206
- console.log(`- ${plugin.name} ${status}`);
207
- });
208
- console.log('=====================================');
209
- console.log(`总计: ${pluginsInfo.length} 个插件\n`);
210
- }
211
-
212
- /**
213
- * 启用插件
214
- * @param {string} pluginName - 插件名称
215
- */
216
- enablePlugin(pluginName) {
217
- // 验证插件名称
218
- if (!validator.validateCommandParam(pluginName)) {
219
- console.error('错误: 插件名称包含无效字符');
220
- return;
221
- }
222
-
223
- // 启用插件
224
- pluginConfig.enablePlugin(pluginName);
225
- console.log(`已启用插件: ${pluginName}`);
226
- console.log('提示: 请重新运行命令以应用更改');
227
- }
228
-
229
- /**
230
- * 禁用插件
231
- * @param {string} pluginName - 插件名称
232
- */
233
- disablePlugin(pluginName) {
234
- // 验证插件名称
235
- if (!validator.validateCommandParam(pluginName)) {
236
- console.error('错误: 插件名称包含无效字符');
237
- return;
238
- }
239
-
240
- // 禁用插件
241
- pluginConfig.disablePlugin(pluginName);
242
- console.log(`已禁用插件: ${pluginName}`);
243
- console.log('提示: 请重新运行命令以应用更改');
244
- }
245
-
246
- /**
247
- * 删除插件
248
- * @param {string} pluginName - 插件名称
249
- */
250
- removePlugin(pluginName) {
251
- // 验证插件名称
252
- if (!validator.validateCommandParam(pluginName)) {
253
- console.error('错误: 插件名称包含无效字符');
254
- return;
255
- }
256
-
257
- const fs = require('fs');
258
- const path = require('path');
259
-
260
- // 插件目录路径
261
- const pluginsDir = path.join(__dirname, '../plugins');
262
-
263
- // 构建插件文件路径
264
- const pluginFileName = `${pluginName}.js`;
265
- const pluginPath = path.join(pluginsDir, pluginFileName);
266
-
267
- // 检查插件文件是否存在
268
- if (!fs.existsSync(pluginPath)) {
269
- console.error(`错误: 插件文件不存在: ${pluginPath}`);
270
- return;
271
- }
272
-
273
- try {
274
- // 删除插件文件
275
- fs.unlinkSync(pluginPath);
276
-
277
- // 从配置中移除插件的禁用状态
278
- pluginConfig.removePluginFromConfig(pluginName);
279
-
280
- console.log(`已成功删除插件: ${pluginName}`);
281
- console.log('提示: 请重新运行命令以应用更改');
282
- } catch (error) {
283
- console.error(`删除插件失败: ${error.message}`);
284
- }
285
- }
286
-
287
329
  /**
288
330
  * 管理插件配置
289
331
  * @param {string} pluginName - 插件名称
@@ -347,20 +389,15 @@ module.exports = ${this.toCamelCase(name)}Plugin;
347
389
  */
348
390
  async addExternalPlugin(url) {
349
391
  try {
350
- const fs = require('fs');
351
- const path = require('path');
352
- const https = require('https');
353
- const http = require('http');
392
+ // 用户插件目录(不会被重新安装影响)
393
+ const userPluginsDir = path.join(process.env.HOME || process.env.USERPROFILE, '.xx-cli', 'plugins');
354
394
 
355
- // 插件目录路径
356
- const pluginsDir = path.join(__dirname, '../plugins');
357
-
358
- // 确保插件目录存在
359
- fs.ensureDirSync(pluginsDir);
395
+ // 确保用户插件目录存在
396
+ fs.ensureDirSync(userPluginsDir);
360
397
 
361
398
  // 提取插件名称
362
399
  const pluginFileName = path.basename(url);
363
- const pluginPath = path.join(pluginsDir, pluginFileName);
400
+ const pluginPath = path.join(userPluginsDir, pluginFileName);
364
401
 
365
402
  // 检查插件是否已存在
366
403
  if (fs.existsSync(pluginPath)) {
@@ -425,7 +462,7 @@ module.exports = ${this.toCamelCase(name)}Plugin;
425
462
 
426
463
  // 提取文件名
427
464
  const pluginFileName = path.basename(url);
428
- const pluginPath = path.join(pluginsDir, pluginFileName);
465
+ const pluginPath = path.join(userPluginsDir, pluginFileName);
429
466
 
430
467
  // 拷贝文件
431
468
  await fs.copy(url, pluginPath);
@@ -435,10 +472,17 @@ module.exports = ${this.toCamelCase(name)}Plugin;
435
472
  console.error('错误: 无效的插件URL或本地文件路径');
436
473
  return;
437
474
  }
475
+
476
+ // 提示用户
477
+ console.log('\n提示:');
478
+ console.log('- 插件已自动注册到命令系统');
479
+ console.log('- 可以使用 "xx plugin list" 命令查看已安装的插件');
480
+ console.log('- 可以使用 "xx <plugin-command>" 命令使用插件功能');
481
+ console.log('- 此插件保存在用户目录中,不会被重新安装影响');
438
482
  } catch (error) {
439
483
  console.error('添加插件失败:', error.message);
440
484
  }
441
485
  }
442
486
  }
443
487
 
444
- module.exports = new CoreCommands();
488
+ module.exports = PluginManagerPlugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xubill/xx-cli",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "个人工具集",
5
5
  "main": "lib/core/index.js",
6
6
  "bin": {