momo-ai 1.0.20 → 1.0.21
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/.claude/skills/algorithmic-art/LICENSE.txt +202 -0
- package/.claude/skills/algorithmic-art/SKILL.md +405 -0
- package/.claude/skills/algorithmic-art/templates/generator_template.js +223 -0
- package/.claude/skills/algorithmic-art/templates/viewer.html +599 -0
- package/.claude/skills/r2mo-rad-lain/PROMPT.md +281 -0
- package/.claude/skills/r2mo-rad-lain/README.md +192 -0
- package/.claude/skills/r2mo-rad-lain/SKILL.md +412 -0
- package/.claude/skills/r2mo-rad-lain/examples/argument-parsing.js +154 -0
- package/.claude/skills/r2mo-rad-lain/examples/file-operations.js +182 -0
- package/.claude/skills/r2mo-rad-lain/file-utils-api.md +281 -0
- package/.claude/skills/r2mo-rad-lain/menu-api.md +187 -0
- package/.claude/skills/r2mo-rad-lain/scripts/file-utils.js +223 -0
- package/.claude/skills/r2mo-rad-lain/scripts/menu.js +289 -0
- package/.claude/skills/r2mo-rad-lain/scripts/yaml-parser.js +209 -0
- package/.claude/skills/r2mo-rad-lain/templates/command.json.template +13 -0
- package/.claude/skills/r2mo-rad-lain/templates/executor.js.template +32 -0
- package/.claude/skills/r2mo-rad-lain/templates/interactive-menu.js.template +221 -0
- package/.cursor/mcp.json +17 -0
- package/.obsidian/app.json +1 -0
- package/.obsidian/appearance.json +4 -0
- package/.obsidian/community-plugins.json +4 -0
- package/.obsidian/core-plugins.json +33 -0
- package/.obsidian/plugins/ai-agent/main.js +98495 -0
- package/.obsidian/plugins/ai-agent/manifest.json +11 -0
- package/.obsidian/plugins/ai-agent/styles.css +806 -0
- package/.obsidian/plugins/dataview/main.js +20876 -0
- package/.obsidian/plugins/dataview/manifest.json +11 -0
- package/.obsidian/plugins/dataview/styles.css +141 -0
- package/.obsidian/plugins/obsidian-excalidraw-plugin/main.js +10 -0
- package/.obsidian/plugins/obsidian-excalidraw-plugin/manifest.json +12 -0
- package/.obsidian/plugins/obsidian-excalidraw-plugin/styles.css +1 -0
- package/.obsidian/plugins/templater-obsidian/main.js +45 -0
- package/.obsidian/plugins/templater-obsidian/manifest.json +11 -0
- package/.obsidian/plugins/templater-obsidian/styles.css +226 -0
- package/.obsidian/plugins/terminal/main.js +200 -0
- package/.obsidian/plugins/terminal/manifest.json +14 -0
- package/.obsidian/plugins/terminal/styles.css +32 -0
- package/.obsidian/themes/AnuPpuccin/manifest.json +7 -0
- package/.obsidian/themes/AnuPpuccin/theme.css +9080 -0
- package/.obsidian/themes/Things/manifest.json +7 -0
- package/.obsidian/themes/Things/theme.css +1628 -0
- package/.obsidian/workspace.json +196 -0
- package/README.md +10 -123
- package/docs/images/logo.jpeg +0 -0
- package/install.sh +1 -0
- package/package.json +6 -2
- package/skills/r2mo-rad-lain/SKILL.md +101 -0
- package/src/_mcp/skills-server.mjs +70 -0
- package/src/_skill/repositories.json +16 -0
- package/src/commander/help.json +5 -0
- package/src/commander/mcp.json +13 -0
- package/src/commander/open.json +8 -2
- package/src/commander/skills.json +20 -0
- package/src/executor/executeEnv.js +48 -38
- package/src/executor/executeHelp.js +77 -16
- package/src/executor/executeInit.js +203 -149
- package/src/executor/executeMcp.js +290 -0
- package/src/executor/executeOpen.js +144 -125
- package/src/executor/executeSkills.js +747 -0
- package/src/executor/index.js +5 -39
- package/src/momo.js +2 -1
- package/src/utils/momo-args.js +39 -0
- package/src/utils/momo-file-utils.js +75 -0
- package/src/utils/momo-menu.js +54 -0
- package/src/commander/actor.json +0 -12
- package/src/commander/actors.json +0 -6
- package/src/commander/add.json +0 -12
- package/src/commander/agent.json +0 -12
- package/src/commander/agentcfg.json +0 -5
- package/src/commander/archive.json +0 -12
- package/src/commander/commit.json +0 -12
- package/src/commander/console.json +0 -7
- package/src/commander/lain.json +0 -7
- package/src/commander/list.json +0 -7
- package/src/commander/plan.json +0 -12
- package/src/commander/project.json +0 -12
- package/src/commander/pull.json +0 -6
- package/src/commander/push.json +0 -6
- package/src/commander/repo.json +0 -18
- package/src/commander/run.json +0 -18
- package/src/commander/show.json +0 -12
- package/src/commander/tasks.json +0 -18
- package/src/commander/unlock.json +0 -6
- package/src/commander/validate.json +0 -12
- package/src/executor/executeActor.js +0 -133
- package/src/executor/executeActors.js +0 -58
- package/src/executor/executeAdd.js +0 -307
- package/src/executor/executeAgent.js +0 -299
- package/src/executor/executeAgentCfg.js +0 -210
- package/src/executor/executeArchive.js +0 -124
- package/src/executor/executeCommit.js +0 -202
- package/src/executor/executeConsole.js +0 -142
- package/src/executor/executeList.js +0 -133
- package/src/executor/executePlan.js +0 -164
- package/src/executor/executeProject.js +0 -313
- package/src/executor/executePull.js +0 -127
- package/src/executor/executePush.js +0 -243
- package/src/executor/executeRepo.js +0 -238
- package/src/executor/executeRun.js +0 -644
- package/src/executor/executeShow.js +0 -164
- package/src/executor/executeTasks.js +0 -384
- package/src/executor/executeUnlock.js +0 -110
- package/src/executor/executeValidate.js +0 -210
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: r2mo-rad-lain
|
|
3
|
+
description: "R2MO RAD 快速应用开发技能 - 专注于 Momo/LAIN 项目的 CLI 命令开发。支持交互式菜单、参数解析、文件操作等功能。用于快速开发终端工具、交互式界面和命令行应用。"
|
|
4
|
+
version: 2.0.0
|
|
5
|
+
category: 🟢 R2MO RAD (快速应用开发)
|
|
6
|
+
tags:
|
|
7
|
+
- r2mo
|
|
8
|
+
- rad
|
|
9
|
+
- lain
|
|
10
|
+
- nodejs
|
|
11
|
+
- cli
|
|
12
|
+
- momo
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# R2MO RAD LAIN 技能
|
|
16
|
+
|
|
17
|
+
## 概述
|
|
18
|
+
|
|
19
|
+
本技能用于开发 Momo/LAIN 项目的 CLI 命令。项目采用 CommonJS 规范,使用轻量级原生风格,不依赖 Commander/Yargs 等重型框架。
|
|
20
|
+
|
|
21
|
+
## 技术栈限制与规范
|
|
22
|
+
|
|
23
|
+
### 必须遵守
|
|
24
|
+
|
|
25
|
+
| 项目 | 要求 | 说明 |
|
|
26
|
+
|------|------|------|
|
|
27
|
+
| **模块规范** | CommonJS | 使用 `require`/`module.exports`,禁止使用 ES Module |
|
|
28
|
+
| **Node 版本** | >= 22.x | 项目要求的最低版本 |
|
|
29
|
+
| **参数解析** | 原生/Epic | 使用 `Ec.parseArgument` 或 `process.argv` |
|
|
30
|
+
| **日志输出** | Epic 工具 | 使用 `Ec.info`/`Ec.warn`/`Ec.error`/`Ec.waiting` |
|
|
31
|
+
| **颜色库** | colors | 已全局配置,使用 `.green`/`.blue` 等 |
|
|
32
|
+
| **用户输入** | Ec.ask | 使用项目统一的 readline 封装 |
|
|
33
|
+
|
|
34
|
+
### 禁止使用
|
|
35
|
+
|
|
36
|
+
| 项目 | 原因 |
|
|
37
|
+
|------|------|
|
|
38
|
+
| Commander.js | 过重,与项目风格不符 |
|
|
39
|
+
| Yargs | 过重,与项目风格不符 |
|
|
40
|
+
| Inquirer.js | 与项目 readline 冲突 |
|
|
41
|
+
| ES Module (import/export) | 项目使用 CommonJS |
|
|
42
|
+
| TypeScript | 项目使用纯 JavaScript |
|
|
43
|
+
|
|
44
|
+
### 推荐使用
|
|
45
|
+
|
|
46
|
+
| 项目 | 场景 |
|
|
47
|
+
|------|------|
|
|
48
|
+
| fs/fs.promises | 文件操作 |
|
|
49
|
+
| path | 路径处理 |
|
|
50
|
+
| os | 系统信息(如 homedir、tmpdir) |
|
|
51
|
+
| child_process | 外部命令执行 |
|
|
52
|
+
| readline | 交互式菜单(仅限本技能脚本) |
|
|
53
|
+
|
|
54
|
+
## 工作流决策树
|
|
55
|
+
|
|
56
|
+
### 开发新命令
|
|
57
|
+
1. 阅读本文档 SKILL.md
|
|
58
|
+
2. 创建命令配置 `src/commander/{command}.json`
|
|
59
|
+
3. 创建执行器 `src/executor/execute{Command}.js`
|
|
60
|
+
4. 在 `src/executor/index.js` 中注册
|
|
61
|
+
|
|
62
|
+
### 需要交互式菜单
|
|
63
|
+
1. **必须阅读**: [`menu-api.md`](menu-api.md) - 完整菜单 API 文档
|
|
64
|
+
2. 使用 `scripts/menu.js` 模块
|
|
65
|
+
3. 菜单完成后再使用 `Ec.ask` 进行确认
|
|
66
|
+
|
|
67
|
+
### 需要文件操作
|
|
68
|
+
1. **必须阅读**: [`file-utils-api.md`](file-utils-api.md) - 文件工具 API 文档
|
|
69
|
+
2. 使用 `scripts/file-utils.js` 模块
|
|
70
|
+
3. 使用 `scripts/yaml-parser.js` 解析 YAML
|
|
71
|
+
|
|
72
|
+
## 项目架构
|
|
73
|
+
|
|
74
|
+
### 目录结构
|
|
75
|
+
```
|
|
76
|
+
src/
|
|
77
|
+
├── commander/ # 命令配置文件 (JSON)
|
|
78
|
+
│ └── {command}.json
|
|
79
|
+
├── executor/ # 命令执行器
|
|
80
|
+
│ ├── index.js # 执行器导出索引
|
|
81
|
+
│ └── execute{Command}.js
|
|
82
|
+
├── epic/ # 核心工具库
|
|
83
|
+
│ ├── index.js # 统一导出
|
|
84
|
+
│ ├── momo.fn.log.js # 日志输出
|
|
85
|
+
│ ├── lain.fn.parse.js # 参数解析
|
|
86
|
+
│ └── ...
|
|
87
|
+
├── _skill/ # 技能配置
|
|
88
|
+
│ └── repositories.json
|
|
89
|
+
└── momo.js # 主入口
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## 命令配置格式
|
|
93
|
+
|
|
94
|
+
在 `src/commander/` 目录下创建 JSON 配置文件:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"executor": "executeMyCommand",
|
|
99
|
+
"description": "命令描述(CV前缀表示会复制到剪切板)",
|
|
100
|
+
"command": "mycommand",
|
|
101
|
+
"options": [
|
|
102
|
+
{
|
|
103
|
+
"name": "param",
|
|
104
|
+
"alias": "p",
|
|
105
|
+
"description": "参数描述",
|
|
106
|
+
"type": "string",
|
|
107
|
+
"default": "默认值"
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**字段说明:**
|
|
114
|
+
- `executor`: 对应 `src/executor/` 中的执行器函数名
|
|
115
|
+
- `description`: 命令描述,`(CV)` 前缀表示会复制内容到剪切板
|
|
116
|
+
- `command`: 命令名称
|
|
117
|
+
- `options`: 参数列表
|
|
118
|
+
- `name`: 参数全名 (--name)
|
|
119
|
+
- `alias`: 参数别名 (-n)
|
|
120
|
+
- `type`: 类型 string/number/boolean
|
|
121
|
+
- `default`: 默认值
|
|
122
|
+
- `required`: 是否必需
|
|
123
|
+
|
|
124
|
+
## 执行器开发
|
|
125
|
+
|
|
126
|
+
### 基础模板
|
|
127
|
+
|
|
128
|
+
参考 `templates/executor.js.template`:
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
const fs = require('fs');
|
|
132
|
+
const path = require('path');
|
|
133
|
+
const Ec = require('../epic');
|
|
134
|
+
const fsAsync = require('fs').promises;
|
|
135
|
+
|
|
136
|
+
module.exports = async (options) => {
|
|
137
|
+
try {
|
|
138
|
+
// 参数解析
|
|
139
|
+
const parsed = Ec.parseArgument(options);
|
|
140
|
+
const paramValue = parsed.param || parsed.p;
|
|
141
|
+
|
|
142
|
+
// 业务逻辑
|
|
143
|
+
Ec.waiting('正在执行...');
|
|
144
|
+
|
|
145
|
+
// 成功退出
|
|
146
|
+
Ec.info('✅ 执行成功!');
|
|
147
|
+
process.exit(0);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
Ec.error(`❌ 执行失败: ${error.message}`);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### 带交互菜单的执行器
|
|
156
|
+
|
|
157
|
+
参考 `templates/interactive-menu.js.template`。
|
|
158
|
+
|
|
159
|
+
**关键:使用本技能提供的菜单脚本**
|
|
160
|
+
|
|
161
|
+
```javascript
|
|
162
|
+
const Ec = require('../epic');
|
|
163
|
+
// 引用技能脚本(需要调整路径或复制到项目中)
|
|
164
|
+
const { selectMultiple } = require('../../skills/r2mo-rad-lain/scripts/menu');
|
|
165
|
+
|
|
166
|
+
module.exports = async (options) => {
|
|
167
|
+
const items = [
|
|
168
|
+
{ name: 'item-1', description: '选项一' },
|
|
169
|
+
{ name: 'item-2', description: '选项二' }
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
// 1. 菜单选择(raw mode)
|
|
173
|
+
const result = await selectMultiple(items, '选择菜单');
|
|
174
|
+
|
|
175
|
+
if (result.items.length === 0) {
|
|
176
|
+
Ec.waiting('未选择任何项');
|
|
177
|
+
process.exit(0);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 2. 确认(普通输入模式,菜单完成后才能使用)
|
|
181
|
+
const answer = await Ec.ask('确认?(y/N): ');
|
|
182
|
+
if (answer.toLowerCase() !== 'y') {
|
|
183
|
+
Ec.waiting('已取消');
|
|
184
|
+
process.exit(0);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// 3. 执行操作
|
|
188
|
+
// ...
|
|
189
|
+
};
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Epic 工具库 API
|
|
193
|
+
|
|
194
|
+
### 日志输出
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
Ec.info('绿色信息'); // [Momo AI] 绿色信息
|
|
198
|
+
Ec.waiting('蓝色执行'); // [Momo AI] 蓝色执行
|
|
199
|
+
Ec.warn('黄色警告'); // [Momo AI] 黄色警告
|
|
200
|
+
Ec.error('红色错误'); // [Momo ER] 红色错误
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### 参数解析
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
const parsed = Ec.parseArgument(options);
|
|
207
|
+
// 支持 --name 和 -n 两种形式
|
|
208
|
+
const value = parsed.name || parsed.n;
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**注意:** `Ec.parseArgument` 要求参数成对出现。对于可选值参数(如 `-r` 可带可不带值),需要自行解析 `process.argv`。
|
|
212
|
+
|
|
213
|
+
### 用户输入
|
|
214
|
+
|
|
215
|
+
```javascript
|
|
216
|
+
const answer = await Ec.ask('请输入: ');
|
|
217
|
+
// 程序退出前关闭
|
|
218
|
+
Ec.askClose();
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 颜色扩展
|
|
222
|
+
|
|
223
|
+
```javascript
|
|
224
|
+
'文本'.green // 绿色
|
|
225
|
+
'文本'.blue // 蓝色
|
|
226
|
+
'文本'.yellow // 黄色
|
|
227
|
+
'文本'.red // 红色
|
|
228
|
+
'文本'.cyan // 青色
|
|
229
|
+
'文本'.gray // 灰色
|
|
230
|
+
'文本'.bold // 加粗
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## UI 排版规范
|
|
234
|
+
|
|
235
|
+
### 动态对齐
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
// 计算最大长度
|
|
239
|
+
const maxLen = Math.max(...items.map(i => i.name.length), 4);
|
|
240
|
+
|
|
241
|
+
// 使用 padEnd 对齐
|
|
242
|
+
items.forEach(item => {
|
|
243
|
+
const name = item.name.padEnd(maxLen);
|
|
244
|
+
console.log(` ${name} ${item.description}`);
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### 分隔线
|
|
249
|
+
|
|
250
|
+
```javascript
|
|
251
|
+
console.log('─'.repeat(50).gray);
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### 视觉层级
|
|
255
|
+
|
|
256
|
+
```javascript
|
|
257
|
+
console.log(''); // 空行分隔区块
|
|
258
|
+
Ec.info('=== 标题 ==='); // 区块标题
|
|
259
|
+
console.log('');
|
|
260
|
+
// 内容...
|
|
261
|
+
console.log('');
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## 开发心得与避坑指南
|
|
265
|
+
|
|
266
|
+
### readline 回显问题
|
|
267
|
+
|
|
268
|
+
**问题:** 输入显示两次
|
|
269
|
+
|
|
270
|
+
**原因:** 同时使用多个 readline 实例,或 raw mode 与普通 readline 混用
|
|
271
|
+
|
|
272
|
+
**解决:**
|
|
273
|
+
1. 使用本技能提供的 `scripts/menu.js`,它正确处理了 raw mode
|
|
274
|
+
2. 菜单选择和确认输入分离:菜单完成后才使用 `Ec.ask`
|
|
275
|
+
3. 不要在 raw mode 中调用 `Ec.ask`
|
|
276
|
+
|
|
277
|
+
### raw mode 管理
|
|
278
|
+
|
|
279
|
+
**问题:** 程序退出后终端异常
|
|
280
|
+
|
|
281
|
+
**原因:** 未正确退出 raw mode
|
|
282
|
+
|
|
283
|
+
**解决:**
|
|
284
|
+
```javascript
|
|
285
|
+
// 所有退出路径都要恢复
|
|
286
|
+
process.stdin.setRawMode(false);
|
|
287
|
+
process.stdin.removeListener('keypress', onKeypress);
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### 异步流程问题
|
|
291
|
+
|
|
292
|
+
**问题:** setTimeout 中的 async 函数未被等待
|
|
293
|
+
|
|
294
|
+
**原因:** Promise 未正确链接
|
|
295
|
+
|
|
296
|
+
**解决:** 使用独立的 async 函数,而不是在 Promise 回调中嵌套 async
|
|
297
|
+
|
|
298
|
+
```javascript
|
|
299
|
+
// 错误
|
|
300
|
+
setTimeout(async () => {
|
|
301
|
+
const answer = await Ec.ask('?');
|
|
302
|
+
resolve(answer); // resolve 可能在 await 之前执行
|
|
303
|
+
}, 100);
|
|
304
|
+
|
|
305
|
+
// 正确:分离成两个函数
|
|
306
|
+
const menuResult = await selectMenu(items); // 菜单
|
|
307
|
+
const answer = await Ec.ask('确认?'); // 确认
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### 参数解析限制
|
|
311
|
+
|
|
312
|
+
**问题:** `Ec.parseArgument` 不支持可选值参数
|
|
313
|
+
|
|
314
|
+
**解决:** 自行解析 `process.argv`
|
|
315
|
+
|
|
316
|
+
```javascript
|
|
317
|
+
const parseOptionalArg = (flagName, aliasName) => {
|
|
318
|
+
const args = process.argv.slice(3);
|
|
319
|
+
let hasFlag = false;
|
|
320
|
+
let value = null;
|
|
321
|
+
|
|
322
|
+
for (let i = 0; i < args.length; i++) {
|
|
323
|
+
if (args[i] === `-${aliasName}` || args[i] === `--${flagName}`) {
|
|
324
|
+
hasFlag = true;
|
|
325
|
+
const next = args[i + 1];
|
|
326
|
+
if (next && !next.startsWith('-')) {
|
|
327
|
+
value = next;
|
|
328
|
+
}
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return { hasFlag, value };
|
|
334
|
+
};
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### 资源清理
|
|
338
|
+
|
|
339
|
+
**问题:** 临时文件未清理
|
|
340
|
+
|
|
341
|
+
**解决:** 使用 try/finally
|
|
342
|
+
|
|
343
|
+
```javascript
|
|
344
|
+
let tempDir = null;
|
|
345
|
+
try {
|
|
346
|
+
tempDir = createTempDir('task');
|
|
347
|
+
// 操作...
|
|
348
|
+
} finally {
|
|
349
|
+
if (tempDir) {
|
|
350
|
+
await cleanup(tempDir);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## 脚本引用
|
|
356
|
+
|
|
357
|
+
本技能提供以下可复用的脚本模块:
|
|
358
|
+
|
|
359
|
+
| 脚本 | 说明 | 文档 |
|
|
360
|
+
|------|------|------|
|
|
361
|
+
| `scripts/menu.js` | 交互式菜单 | [menu-api.md](menu-api.md) |
|
|
362
|
+
| `scripts/file-utils.js` | 文件操作工具 | [file-utils-api.md](file-utils-api.md) |
|
|
363
|
+
| `scripts/yaml-parser.js` | YAML 解析器 | [file-utils-api.md](file-utils-api.md) |
|
|
364
|
+
|
|
365
|
+
**使用方式:**
|
|
366
|
+
|
|
367
|
+
1. **复制到项目中使用**(推荐)
|
|
368
|
+
```bash
|
|
369
|
+
cp -r skills/r2mo-rad-lain/scripts src/_scripts
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
2. **直接引用**(开发阶段)
|
|
373
|
+
```javascript
|
|
374
|
+
const { selectMultiple } = require('../../skills/r2mo-rad-lain/scripts/menu');
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## 注册执行器
|
|
378
|
+
|
|
379
|
+
在 `src/executor/index.js` 中添加:
|
|
380
|
+
|
|
381
|
+
```javascript
|
|
382
|
+
const executeMyCommand = require('./executeMyCommand');
|
|
383
|
+
|
|
384
|
+
const exported = {
|
|
385
|
+
// ... 其他执行器
|
|
386
|
+
executeMyCommand
|
|
387
|
+
};
|
|
388
|
+
module.exports = exported;
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## 测试命令
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
# 运行命令
|
|
395
|
+
node src/momo.js mycommand
|
|
396
|
+
|
|
397
|
+
# 带参数
|
|
398
|
+
node src/momo.js mycommand -p value
|
|
399
|
+
|
|
400
|
+
# 查看帮助
|
|
401
|
+
node src/momo.js help -c mycommand
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
## 依赖说明
|
|
405
|
+
|
|
406
|
+
本技能使用的依赖(项目已包含):
|
|
407
|
+
|
|
408
|
+
- **colors**: 终端颜色支持
|
|
409
|
+
- **co**: 生成器函数执行
|
|
410
|
+
- **commander**: 命令行框架(仅用于底层,执行器不直接使用)
|
|
411
|
+
- **underscore**: 工具函数
|
|
412
|
+
- **immutable**: 不可变数据结构
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 参数解析示例
|
|
3
|
+
* 演示如何处理复杂的命令行参数
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================
|
|
7
|
+
// 使用 Ec.parseArgument(标准方式)
|
|
8
|
+
// ============================================================
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 标准参数解析
|
|
12
|
+
* 适用于 key-value 成对参数
|
|
13
|
+
*
|
|
14
|
+
* 命令示例:momo cmd -n value --name value
|
|
15
|
+
*/
|
|
16
|
+
const standardParsing = (options) => {
|
|
17
|
+
const Ec = require('../epic');
|
|
18
|
+
const parsed = Ec.parseArgument(options);
|
|
19
|
+
|
|
20
|
+
// 支持 --name 和 -n 两种形式
|
|
21
|
+
const name = parsed.name || parsed.n;
|
|
22
|
+
const type = parsed.type || parsed.t;
|
|
23
|
+
|
|
24
|
+
return { name, type };
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// ============================================================
|
|
28
|
+
// 自定义参数解析(支持可选值)
|
|
29
|
+
// ============================================================
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 解析可选值参数
|
|
33
|
+
* 适用于参数值可以省略的情况
|
|
34
|
+
*
|
|
35
|
+
* 命令示例:
|
|
36
|
+
* momo cmd -r -> hasFlag: true, value: null
|
|
37
|
+
* momo cmd -r value -> hasFlag: true, value: 'value'
|
|
38
|
+
* momo cmd -> hasFlag: false, value: null
|
|
39
|
+
*/
|
|
40
|
+
const parseOptionalArg = (flagName, aliasName) => {
|
|
41
|
+
const args = process.argv.slice(3);
|
|
42
|
+
let hasFlag = false;
|
|
43
|
+
let value = null;
|
|
44
|
+
|
|
45
|
+
for (let i = 0; i < args.length; i++) {
|
|
46
|
+
const arg = args[i];
|
|
47
|
+
if (arg === `-${aliasName}` || arg === `--${flagName}`) {
|
|
48
|
+
hasFlag = true;
|
|
49
|
+
// 检查下一个参数是否是值(不以 - 开头)
|
|
50
|
+
const nextArg = args[i + 1];
|
|
51
|
+
if (nextArg && !nextArg.startsWith('-')) {
|
|
52
|
+
value = nextArg;
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { hasFlag, value };
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// ============================================================
|
|
62
|
+
// 布尔标志解析
|
|
63
|
+
// ============================================================
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 解析布尔标志
|
|
67
|
+
*
|
|
68
|
+
* 命令示例:
|
|
69
|
+
* momo cmd --verbose -> true
|
|
70
|
+
* momo cmd -v -> true
|
|
71
|
+
* momo cmd -> false
|
|
72
|
+
*/
|
|
73
|
+
const parseBooleanFlag = (flagName, aliasName) => {
|
|
74
|
+
const args = process.argv.slice(3);
|
|
75
|
+
return args.includes(`--${flagName}`) || args.includes(`-${aliasName}`);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// ============================================================
|
|
79
|
+
// 位置参数解析
|
|
80
|
+
// ============================================================
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 解析位置参数
|
|
84
|
+
*
|
|
85
|
+
* 命令示例:
|
|
86
|
+
* momo cmd arg1 arg2 -> ['arg1', 'arg2']
|
|
87
|
+
*/
|
|
88
|
+
const parsePositionalArgs = () => {
|
|
89
|
+
const args = process.argv.slice(3);
|
|
90
|
+
const positional = [];
|
|
91
|
+
|
|
92
|
+
let i = 0;
|
|
93
|
+
while (i < args.length) {
|
|
94
|
+
const arg = args[i];
|
|
95
|
+
if (arg.startsWith('-')) {
|
|
96
|
+
// 跳过 flag 和它的值
|
|
97
|
+
i += 2;
|
|
98
|
+
} else {
|
|
99
|
+
positional.push(arg);
|
|
100
|
+
i++;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return positional;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// ============================================================
|
|
108
|
+
// 完整示例
|
|
109
|
+
// ============================================================
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* 综合参数解析示例
|
|
113
|
+
*
|
|
114
|
+
* 命令格式:momo mycommand [target] -n name -r [repo] --verbose
|
|
115
|
+
*/
|
|
116
|
+
const parseAllArgs = (options) => {
|
|
117
|
+
// 1. 标准参数
|
|
118
|
+
const Ec = require('../epic');
|
|
119
|
+
let parsed = {};
|
|
120
|
+
try {
|
|
121
|
+
parsed = Ec.parseArgument(options);
|
|
122
|
+
} catch (e) {
|
|
123
|
+
// 忽略缺少值的错误,手动处理
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 2. 可选值参数
|
|
127
|
+
const remote = parseOptionalArg('remote', 'r');
|
|
128
|
+
|
|
129
|
+
// 3. 布尔标志
|
|
130
|
+
const verbose = parseBooleanFlag('verbose', 'v');
|
|
131
|
+
|
|
132
|
+
// 4. 位置参数
|
|
133
|
+
const positional = parsePositionalArgs();
|
|
134
|
+
const target = positional[0] || '.';
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
name: parsed.name || parsed.n,
|
|
138
|
+
remote: remote,
|
|
139
|
+
verbose: verbose,
|
|
140
|
+
target: target
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// ============================================================
|
|
145
|
+
// 导出
|
|
146
|
+
// ============================================================
|
|
147
|
+
|
|
148
|
+
module.exports = {
|
|
149
|
+
standardParsing,
|
|
150
|
+
parseOptionalArg,
|
|
151
|
+
parseBooleanFlag,
|
|
152
|
+
parsePositionalArgs,
|
|
153
|
+
parseAllArgs
|
|
154
|
+
};
|