momo-ai 1.0.20 → 1.0.22
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/SKILL.md +101 -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/.trae/skills/algorithmic-art/LICENSE.txt +202 -0
- package/.trae/skills/algorithmic-art/SKILL.md +405 -0
- package/.trae/skills/algorithmic-art/templates/generator_template.js +223 -0
- package/.trae/skills/algorithmic-art/templates/viewer.html +599 -0
- package/.trae/skills/doc-coauthoring/SKILL.md +375 -0
- package/.trae/skills/frontend-design/LICENSE.txt +177 -0
- package/.trae/skills/frontend-design/SKILL.md +42 -0
- package/.trae/skills/r2mo-rad-lain/SKILL.md +101 -0
- package/README.md +12 -148
- package/docs/images/logo.jpeg +0 -0
- package/docs/images/r2mo-lain.png +0 -0
- package/install.sh +1 -0
- package/package.json +15 -11
- package/skills/r2mo-rad-domain/SKILL.md +70 -0
- package/skills/r2mo-rad-lain/SKILL.md +101 -0
- package/src/_mcp/skills-server.mjs +70 -0
- package/src/_skill/repositories.json +22 -0
- package/src/_template/LAIN/.obsidian/app.json +1 -0
- package/src/_template/LAIN/.obsidian/appearance.json +10 -0
- package/src/_template/LAIN/.obsidian/community-plugins.json +7 -0
- package/src/_template/LAIN/.obsidian/core-plugins.json +33 -0
- package/src/_template/LAIN/.obsidian/plugins/dataview/main.js +20876 -0
- package/src/_template/LAIN/.obsidian/plugins/dataview/manifest.json +11 -0
- package/src/_template/LAIN/.obsidian/plugins/dataview/styles.css +141 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/data.json +815 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/main.js +10 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/manifest.json +12 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/styles.css +1 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-kanban/main.js +153 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-kanban/manifest.json +11 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-kanban/styles.css +1 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-plantuml/main.js +7732 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-plantuml/manifest.json +10 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-plantuml/styles.css +38 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-tasks-plugin/main.js +504 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-tasks-plugin/manifest.json +12 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-tasks-plugin/styles.css +1 -0
- package/src/_template/LAIN/.obsidian/snippets/body-font.css +27 -0
- package/src/_template/LAIN/.obsidian/themes/Primary/manifest.json +9 -0
- package/src/_template/LAIN/.obsidian/themes/Primary/theme.css +3878 -0
- package/src/_template/LAIN/.obsidian/themes/Retro Windows/manifest.json +7 -0
- package/src/_template/LAIN/.obsidian/themes/Retro Windows/theme.css +582 -0
- package/src/_template/LAIN/.obsidian/themes/RetroOS 98/manifest.json +9 -0
- package/src/_template/LAIN/.obsidian/themes/RetroOS 98/theme.css +2566 -0
- package/src/_template/LAIN/.obsidian/types.json +28 -0
- package/src/_template/LAIN/.obsidian/workspace.json +184 -0
- package/src/_template/LAIN/AGENTS.md +170 -16
- package/src/_template/R2MO/domain-enhance.md +10 -0
- package/src/commander/app.json +13 -0
- package/src/commander/apply.json +13 -0
- package/src/commander/ask.json +6 -0
- package/src/commander/docs.json +13 -0
- package/src/commander/domain.json +19 -0
- package/src/commander/help.json +5 -0
- package/src/commander/init.json +1 -1
- package/src/commander/mcp.json +13 -0
- package/src/commander/mmr0.json +6 -0
- package/src/commander/mmr2.json +6 -0
- package/src/commander/open.json +8 -2
- package/src/executor/executeApp.js +133 -0
- package/src/executor/executeApply.js +611 -0
- package/src/executor/executeAsk.js +274 -0
- package/src/executor/executeDocs.js +498 -0
- package/src/executor/executeDomain.js +293 -0
- package/src/executor/executeEnv.js +48 -38
- package/src/executor/executeHelp.js +77 -16
- package/src/executor/executeInit.js +176 -346
- package/src/executor/executeMcp.js +363 -0
- package/src/executor/executeMmr0.js +488 -0
- package/src/executor/executeMmr2.js +880 -0
- package/src/executor/executeOpen.js +144 -125
- package/src/executor/index.js +17 -39
- package/src/momo.js +2 -1
- package/src/python/r2mo_proto.py +418 -0
- package/src/python/r2mo_proto_database.py +369 -0
- package/src/python/r2mo_proto_domain.py +458 -0
- package/src/utils/momo-args.js +39 -0
- package/src/utils/momo-file-utils.js +75 -0
- package/src/utils/momo-menu.js +84 -0
- package/src/_template/LAIN/.momo/advanced/actor.md +0 -42
- package/src/_template/LAIN/.momo/advanced/refer.json +0 -46
- package/src/_template/LAIN/.momo/scripts/submodule-clean.sh +0 -56
- package/src/_template/LAIN/changes/proposal.md +0 -39
- package/src/_template/LAIN/changes/tasks/task-detail.md +0 -45
- package/src/_template/LAIN/changes/tasks.md +0 -49
- package/src/_template/LAIN/execute/admin-n-f-dashboard.md +0 -53
- package/src/_template/LAIN/execute/admin-n-f-form.md +0 -51
- package/src/_template/LAIN/execute/admin-n-f-home.md +0 -49
- package/src/_template/LAIN/execute/admin-n-f-list.md +0 -52
- package/src/_template/LAIN/execute/admin-n-f-login.md +0 -56
- package/src/_template/LAIN/specification/project-model.md +0 -13
- package/src/_template/LAIN/specification/project.md +0 -73
- package/src/_template/LAIN/specification/requirement.md +0 -25
- 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
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const util = require('util');
|
|
4
|
-
const Ec = require('../epic');
|
|
5
|
-
const fsAsync = require('fs').promises;
|
|
6
|
-
const { outCopy } = require('../epic/momo.fn.out');
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 读取模板文件并提取 <!-- BEGIN --> 到 <!-- END --> 之间的内容
|
|
10
|
-
* @param {string} templatePath 模板文件路径
|
|
11
|
-
* @returns {Promise<string>} 提取的模板内容
|
|
12
|
-
*/
|
|
13
|
-
const _readTemplate = async (templatePath) => {
|
|
14
|
-
try {
|
|
15
|
-
const content = await fsAsync.readFile(templatePath, 'utf8');
|
|
16
|
-
const lines = content.split('\n');
|
|
17
|
-
let beginIndex = -1;
|
|
18
|
-
let endIndex = -1;
|
|
19
|
-
|
|
20
|
-
for (let i = 0; i < lines.length; i++) {
|
|
21
|
-
if (lines[i].includes('<!-- BEGIN -->')) {
|
|
22
|
-
beginIndex = i;
|
|
23
|
-
} else if (lines[i].includes('<!-- END -->')) {
|
|
24
|
-
endIndex = i;
|
|
25
|
-
break;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (beginIndex !== -1 && endIndex !== -1) {
|
|
30
|
-
// 提取 BEGIN 和 END 之间的内容(不包括 BEGIN 和 END 行)
|
|
31
|
-
return lines.slice(beginIndex + 1, endIndex).join('\n');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return content;
|
|
35
|
-
} catch (error) {
|
|
36
|
-
Ec.waiting(`读取模板文件失败: ${error.message}`);
|
|
37
|
-
if (process.platform === 'win32') {
|
|
38
|
-
Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
|
|
39
|
-
}
|
|
40
|
-
throw error;
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* 渲染 EJS 模板
|
|
46
|
-
* @param {string} template 模板内容
|
|
47
|
-
* @param {Object} data 数据对象
|
|
48
|
-
* @returns {string} 渲染后的内容
|
|
49
|
-
*/
|
|
50
|
-
const _renderTemplate = (template, data) => {
|
|
51
|
-
let result = template;
|
|
52
|
-
for (const key in data) {
|
|
53
|
-
if (data.hasOwnProperty(key)) {
|
|
54
|
-
const regex = new RegExp(`<%=\\s*${key}\\s*%>`, 'g');
|
|
55
|
-
result = result.replace(regex, data[key]);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return result;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* 检查需求目录是否已存在
|
|
63
|
-
* @param {string} changesDir changes目录路径
|
|
64
|
-
* @param {string} requirementName 需求名称
|
|
65
|
-
* @returns {boolean} 是否存在
|
|
66
|
-
*/
|
|
67
|
-
const _isRequirementExists = (changesDir, requirementName) => {
|
|
68
|
-
const requirementDir = path.join(changesDir, requirementName);
|
|
69
|
-
return fs.existsSync(requirementDir);
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* 从 Markdown 文件中提取第一个标题作为需求名称
|
|
74
|
-
* @param {string} filePath 文件路径
|
|
75
|
-
* @returns {Promise<string>} 第一个标题内容
|
|
76
|
-
*/
|
|
77
|
-
const _extractTitleFromMarkdown = async (filePath) => {
|
|
78
|
-
try {
|
|
79
|
-
const content = await fsAsync.readFile(filePath, 'utf8');
|
|
80
|
-
const lines = content.split('\n');
|
|
81
|
-
|
|
82
|
-
for (const line of lines) {
|
|
83
|
-
// 匹配 # 开头的标题行
|
|
84
|
-
const titleMatch = line.match(/^#\s+(.+)$/);
|
|
85
|
-
if (titleMatch) {
|
|
86
|
-
return titleMatch[1].trim();
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// 如果没有找到标题,使用文件名(不含扩展名)作为需求名称
|
|
91
|
-
const fileName = path.basename(filePath);
|
|
92
|
-
return fileName.replace(/\.[^/.]+$/, ""); // 移除扩展名
|
|
93
|
-
} catch (error) {
|
|
94
|
-
Ec.waiting(`读取文件失败: ${error.message}`);
|
|
95
|
-
throw error;
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* 创建需求目录并拷贝模板文件
|
|
101
|
-
* @param {string} templateDir 模板目录
|
|
102
|
-
* @param {string} targetDir 目标目录
|
|
103
|
-
* @param {string} sourceFilePath 源文件路径(可选)
|
|
104
|
-
*/
|
|
105
|
-
const _copyTemplateFiles = async (templateDir, targetDir, sourceFilePath = null) => {
|
|
106
|
-
try {
|
|
107
|
-
// 确保目标目录存在
|
|
108
|
-
if (!fs.existsSync(targetDir)) {
|
|
109
|
-
await fsAsync.mkdir(targetDir, { recursive: true });
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
let proposalSource = path.join(templateDir, 'proposal.md');
|
|
113
|
-
const proposalTarget = path.join(targetDir, 'proposal.md');
|
|
114
|
-
|
|
115
|
-
// 如果提供了源文件路径,则使用源文件内容覆盖 proposal.md
|
|
116
|
-
if (sourceFilePath && fs.existsSync(sourceFilePath)) {
|
|
117
|
-
proposalSource = sourceFilePath;
|
|
118
|
-
Ec.waiting(`从文件 "${sourceFilePath}" 拷贝内容到 proposal.md`);
|
|
119
|
-
await fsAsync.copyFile(sourceFilePath, proposalTarget);
|
|
120
|
-
} else if (fs.existsSync(proposalSource)) {
|
|
121
|
-
Ec.waiting(`拷贝文件: ${proposalTarget}`);
|
|
122
|
-
await fsAsync.copyFile(proposalSource, proposalTarget);
|
|
123
|
-
} else {
|
|
124
|
-
// 如果模板文件不存在,则创建空文件
|
|
125
|
-
Ec.waiting(`创建空文件: ${proposalTarget}`);
|
|
126
|
-
await fsAsync.writeFile(proposalTarget, '');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// 拷贝 tasks.md
|
|
130
|
-
const tasksSource = path.join(templateDir, 'tasks.md');
|
|
131
|
-
const tasksTarget = path.join(targetDir, 'tasks.md');
|
|
132
|
-
if (fs.existsSync(tasksSource)) {
|
|
133
|
-
Ec.waiting(`拷贝文件: ${tasksTarget}`);
|
|
134
|
-
await fsAsync.copyFile(tasksSource, tasksTarget);
|
|
135
|
-
} else {
|
|
136
|
-
// 如果模板文件不存在,则创建空文件
|
|
137
|
-
Ec.waiting(`创建空文件: ${tasksTarget}`);
|
|
138
|
-
await fsAsync.writeFile(tasksTarget, '');
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// 创建 tasks 目录
|
|
142
|
-
const tasksDir = path.join(targetDir, 'tasks');
|
|
143
|
-
if (!fs.existsSync(tasksDir)) {
|
|
144
|
-
Ec.waiting(`创建目录: ${tasksDir}`);
|
|
145
|
-
await fsAsync.mkdir(tasksDir, { recursive: true });
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// 拷贝 task-detail.md 到 tasks 目录
|
|
149
|
-
const taskDetailSource = path.join(templateDir, 'tasks', 'task-detail.md');
|
|
150
|
-
const taskDetailTarget = path.join(tasksDir, 'M01-T001-01.md'); // 示例任务文件名
|
|
151
|
-
if (fs.existsSync(taskDetailSource)) {
|
|
152
|
-
Ec.waiting(`拷贝文件: ${taskDetailTarget}`);
|
|
153
|
-
await fsAsync.copyFile(taskDetailSource, taskDetailTarget);
|
|
154
|
-
} else {
|
|
155
|
-
// 如果模板文件不存在,则创建空文件
|
|
156
|
-
Ec.waiting(`创建空文件: ${taskDetailTarget}`);
|
|
157
|
-
await fsAsync.writeFile(taskDetailTarget, '');
|
|
158
|
-
}
|
|
159
|
-
} catch (error) {
|
|
160
|
-
Ec.waiting(`拷贝模板文件失败: ${error.message}`);
|
|
161
|
-
if (process.platform === 'win32') {
|
|
162
|
-
Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
|
|
163
|
-
}
|
|
164
|
-
throw error;
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* 将内容复制到剪贴板(去除换行符)
|
|
170
|
-
* @param {string} content 要复制的内容
|
|
171
|
-
*/
|
|
172
|
-
const _copyToClipboard = async (content) => {
|
|
173
|
-
try {
|
|
174
|
-
// 去除换行符,将内容合并为一行
|
|
175
|
-
const contentWithoutNewlines = content.replace(/\r?\n|\r/g, ' ');
|
|
176
|
-
|
|
177
|
-
// 使用统一的剪贴板函数
|
|
178
|
-
await outCopy(contentWithoutNewlines);
|
|
179
|
-
Ec.waiting('✅ 提示词已复制到剪贴板');
|
|
180
|
-
} catch (error) {
|
|
181
|
-
Ec.waiting(`复制到剪贴板失败: ${error.message}`);
|
|
182
|
-
// 提供备选方案
|
|
183
|
-
Ec.waiting('提示词内容:');
|
|
184
|
-
console.log(content);
|
|
185
|
-
if (process.platform === 'win32') {
|
|
186
|
-
Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
module.exports = async (options) => {
|
|
192
|
-
// 参数提取
|
|
193
|
-
const parsed = Ec.parseArgument(options);
|
|
194
|
-
|
|
195
|
-
// 获取需求名称
|
|
196
|
-
let requirementName = parsed.name || parsed.n;
|
|
197
|
-
|
|
198
|
-
// 验证参数
|
|
199
|
-
if (!requirementName) {
|
|
200
|
-
Ec.error("❌ 请提供需求名称或需求文件路径 (-n, --name)");
|
|
201
|
-
process.exit(1);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// 检查是否包含扩展名
|
|
205
|
-
const hasExtension = path.extname(requirementName) !== '';
|
|
206
|
-
|
|
207
|
-
// 如果包含扩展名,视为文件路径处理
|
|
208
|
-
let sourceFilePath = null;
|
|
209
|
-
if (hasExtension) {
|
|
210
|
-
// 检查文件是否存在
|
|
211
|
-
if (!fs.existsSync(requirementName)) {
|
|
212
|
-
// 检查是否在当前目录下存在
|
|
213
|
-
const fullPath = path.resolve(process.cwd(), requirementName);
|
|
214
|
-
if (!fs.existsSync(fullPath)) {
|
|
215
|
-
Ec.error(`❌ 文件 "${requirementName}" 不存在`);
|
|
216
|
-
process.exit(1);
|
|
217
|
-
}
|
|
218
|
-
sourceFilePath = fullPath;
|
|
219
|
-
} else {
|
|
220
|
-
sourceFilePath = requirementName;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// 从文件中提取标题作为需求名称
|
|
224
|
-
requirementName = await _extractTitleFromMarkdown(sourceFilePath);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// 检查需求名称是否包含非法字符(Windows 上特别检查)
|
|
228
|
-
if (process.platform === 'win32') {
|
|
229
|
-
const illegalChars = /[<>:"/\\|?*\x00-\x1F]/g;
|
|
230
|
-
if (illegalChars.test(requirementName)) {
|
|
231
|
-
Ec.error(`❌ 需求名称 "${requirementName}" 包含非法字符,请避免使用以下字符: < > : " / \\ | ? * 以及控制字符`);
|
|
232
|
-
process.exit(1);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// 检查需求名称是否包含点号(除了允许的独立需求)
|
|
237
|
-
if (!hasExtension && requirementName.includes('.')) {
|
|
238
|
-
Ec.error("❌ 独立需求名称不能包含点号(.),以防止与文件扩展名混淆");
|
|
239
|
-
process.exit(1);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
Ec.waiting(`准备添加新需求: ${requirementName}`);
|
|
243
|
-
|
|
244
|
-
try {
|
|
245
|
-
// 获取项目路径
|
|
246
|
-
const projectDir = process.cwd();
|
|
247
|
-
const changesDir = path.join(projectDir, 'specification', 'changes');
|
|
248
|
-
const templateDir = path.join(__dirname, '../_template/LAIN/changes');
|
|
249
|
-
const promptTemplatePath = path.join(__dirname, '../_template/PROMPT/add.md.ejs');
|
|
250
|
-
|
|
251
|
-
// 检查需求是否已存在
|
|
252
|
-
if (_isRequirementExists(changesDir, requirementName)) {
|
|
253
|
-
Ec.error(`❌ 需求 "${requirementName}" 已存在`);
|
|
254
|
-
process.exit(1);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// 确保 changes 目录存在
|
|
258
|
-
if (!fs.existsSync(changesDir)) {
|
|
259
|
-
Ec.waiting(`创建目录: ${changesDir}`);
|
|
260
|
-
try {
|
|
261
|
-
await fsAsync.mkdir(changesDir, { recursive: true });
|
|
262
|
-
} catch (mkdirError) {
|
|
263
|
-
Ec.error(`❌ 创建目录失败: ${mkdirError.message}`);
|
|
264
|
-
if (process.platform === 'win32') {
|
|
265
|
-
Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
|
|
266
|
-
}
|
|
267
|
-
process.exit(1);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// 创建需求目录
|
|
272
|
-
const requirementDir = path.join(changesDir, requirementName);
|
|
273
|
-
Ec.waiting(`创建需求目录: ${requirementDir}`);
|
|
274
|
-
try {
|
|
275
|
-
await fsAsync.mkdir(requirementDir, { recursive: true });
|
|
276
|
-
} catch (mkdirError) {
|
|
277
|
-
Ec.error(`❌ 创建目录失败: ${mkdirError.message}`);
|
|
278
|
-
if (process.platform === 'win32') {
|
|
279
|
-
Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
|
|
280
|
-
// 检查是否是由于特殊字符导致的问题
|
|
281
|
-
if (mkdirError.message.includes('Invalid argument') || mkdirError.message.includes('EINVAL')) {
|
|
282
|
-
Ec.waiting('💡 可能是由于需求名称包含非法字符,请尝试使用更简单的名称');
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
process.exit(1);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// 拷贝模板文件
|
|
289
|
-
await _copyTemplateFiles(templateDir, requirementDir, sourceFilePath);
|
|
290
|
-
|
|
291
|
-
// 读取并处理提示词模板
|
|
292
|
-
const templateContent = await _readTemplate(promptTemplatePath);
|
|
293
|
-
const renderedContent = _renderTemplate(templateContent, { NAME: requirementName });
|
|
294
|
-
|
|
295
|
-
// 将提示词复制到剪贴板
|
|
296
|
-
await _copyToClipboard(renderedContent);
|
|
297
|
-
|
|
298
|
-
Ec.info(`✅ 成功添加新需求 "${requirementName}"`);
|
|
299
|
-
process.exit(0);
|
|
300
|
-
} catch (error) {
|
|
301
|
-
Ec.error(`❌ 执行过程中发生错误: ${error.message}`);
|
|
302
|
-
if (process.platform === 'win32') {
|
|
303
|
-
Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
|
|
304
|
-
}
|
|
305
|
-
process.exit(1);
|
|
306
|
-
}
|
|
307
|
-
};
|
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const util = require('util');
|
|
4
|
-
const Ec = require('../epic');
|
|
5
|
-
|
|
6
|
-
// 将 fs 方法转换为 Promise 版本
|
|
7
|
-
const fsAsync = {
|
|
8
|
-
readFile: util.promisify(fs.readFile),
|
|
9
|
-
readdir: util.promisify(fs.readdir),
|
|
10
|
-
stat: util.promisify(fs.stat)
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* 获取所有 agents 信息
|
|
15
|
-
* @returns {Array} agents 信息数组
|
|
16
|
-
*/
|
|
17
|
-
const _getAgents = () => {
|
|
18
|
-
const agentsDir = path.resolve(__dirname, '../_agent');
|
|
19
|
-
const agents = [];
|
|
20
|
-
|
|
21
|
-
if (!fs.existsSync(agentsDir)) {
|
|
22
|
-
return agents;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// 遍历 _agent 目录下的所有子目录
|
|
26
|
-
const types = fs.readdirSync(agentsDir).filter(file =>
|
|
27
|
-
fs.statSync(path.join(agentsDir, file)).isDirectory()
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
// 遍历每个类型目录
|
|
31
|
-
types.forEach(type => {
|
|
32
|
-
const typeDir = path.join(agentsDir, type);
|
|
33
|
-
const files = fs.readdirSync(typeDir).filter(file => file.endsWith('.json'));
|
|
34
|
-
|
|
35
|
-
// 遍历每个 json 文件
|
|
36
|
-
files.forEach(file => {
|
|
37
|
-
try {
|
|
38
|
-
const filePath = path.join(typeDir, file);
|
|
39
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
40
|
-
const agentInfo = JSON.parse(content);
|
|
41
|
-
|
|
42
|
-
agents.push({
|
|
43
|
-
argName: file.replace('momo-', '').replace('.json', ''),
|
|
44
|
-
type: type,
|
|
45
|
-
id: agentInfo.id,
|
|
46
|
-
name: agentInfo.name,
|
|
47
|
-
uri: agentInfo.uri,
|
|
48
|
-
uriCn: agentInfo['uri-cn'], // 添加CN链接
|
|
49
|
-
filePath: filePath
|
|
50
|
-
});
|
|
51
|
-
} catch (error) {
|
|
52
|
-
Ec.waiting(`⚠️ 无法解析文件: ${file} - ${error.message}`);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
return agents;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* 从 EJS 模板中提取内容
|
|
62
|
-
* @param {string} templatePath 模板路径
|
|
63
|
-
* @returns {Promise<string>} 提取的模板内容
|
|
64
|
-
*/
|
|
65
|
-
const _extractTemplateContent = async (templatePath) => {
|
|
66
|
-
try {
|
|
67
|
-
const content = await fsAsync.readFile(templatePath, 'utf8');
|
|
68
|
-
const lines = content.split('\n');
|
|
69
|
-
let beginIndex = -1;
|
|
70
|
-
let endIndex = -1;
|
|
71
|
-
|
|
72
|
-
for (let i = 0; i < lines.length; i++) {
|
|
73
|
-
if (lines[i].includes('<!-- BEGIN -->')) {
|
|
74
|
-
beginIndex = i;
|
|
75
|
-
} else if (lines[i].includes('<!-- END -->')) {
|
|
76
|
-
endIndex = i;
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (beginIndex !== -1 && endIndex !== -1) {
|
|
82
|
-
// 提取 BEGIN 和 END 之间的内容(不包括 BEGIN 和 END 行)
|
|
83
|
-
return lines.slice(beginIndex + 1, endIndex).join('\n');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return content;
|
|
87
|
-
} catch (error) {
|
|
88
|
-
throw new Error(`无法读取模板文件: ${error.message}`);
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* 将内容复制到剪贴板
|
|
94
|
-
* @param {string} content 要复制的内容
|
|
95
|
-
*/
|
|
96
|
-
const _copyToClipboard = async (content) => {
|
|
97
|
-
const { outCopy } = require('../epic/momo.fn.out');
|
|
98
|
-
try {
|
|
99
|
-
// 先打印内容,按行打印并保留前缀
|
|
100
|
-
Ec.waiting('📄 提示词内容:');
|
|
101
|
-
Ec.waiting('----------------------------------------');
|
|
102
|
-
const lines = content.split('\n');
|
|
103
|
-
lines.forEach(line => {
|
|
104
|
-
Ec.waiting(line);
|
|
105
|
-
});
|
|
106
|
-
Ec.waiting('----------------------------------------');
|
|
107
|
-
|
|
108
|
-
// 再复制到剪贴板
|
|
109
|
-
await outCopy(content);
|
|
110
|
-
Ec.waiting('✅ 提示词已复制到剪贴板');
|
|
111
|
-
} catch (error) {
|
|
112
|
-
Ec.waiting(`复制到剪贴板失败: ${error.message}`);
|
|
113
|
-
// 提供备选方案
|
|
114
|
-
Ec.waiting('提示词内容:');
|
|
115
|
-
const lines = content.split('\n');
|
|
116
|
-
lines.forEach(line => {
|
|
117
|
-
Ec.waiting(line);
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
module.exports = async (options) => {
|
|
123
|
-
// 参数提取
|
|
124
|
-
const parsed = Ec.parseArgument(options);
|
|
125
|
-
const agentName = parsed.agent || parsed.a;
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
// 获取所有 agents
|
|
129
|
-
const agents = _getAgents();
|
|
130
|
-
|
|
131
|
-
if (agents.length === 0) {
|
|
132
|
-
Ec.error("❌ 未找到任何 agents");
|
|
133
|
-
Ec.askClose();
|
|
134
|
-
process.exit(1);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// 如果没有提供 agent 参数,显示可用的 agents 并让用户选择
|
|
138
|
-
if (!agentName) {
|
|
139
|
-
while (true) {
|
|
140
|
-
Ec.waiting('可用的 Agents:');
|
|
141
|
-
|
|
142
|
-
// 计算列宽以适应内容
|
|
143
|
-
const typeColumnWidth = 12;
|
|
144
|
-
const idColumnWidth = 20;
|
|
145
|
-
const uriColumnWidth = 35;
|
|
146
|
-
const uriCnColumnWidth = 35;
|
|
147
|
-
const nameColumnWidth = 35;
|
|
148
|
-
|
|
149
|
-
// 表头
|
|
150
|
-
const header = "编号".padEnd(6) +
|
|
151
|
-
"Type".padEnd(typeColumnWidth) +
|
|
152
|
-
"ID".padEnd(idColumnWidth) +
|
|
153
|
-
"URI".padEnd(uriColumnWidth) +
|
|
154
|
-
"URI-CN".padEnd(uriCnColumnWidth) +
|
|
155
|
-
"Name".padEnd(nameColumnWidth);
|
|
156
|
-
Ec.waiting(header);
|
|
157
|
-
|
|
158
|
-
// 分隔线
|
|
159
|
-
const separator = "-".repeat(5) + " " +
|
|
160
|
-
"-".repeat(typeColumnWidth - 1) + " " +
|
|
161
|
-
"-".repeat(idColumnWidth - 1) + " " +
|
|
162
|
-
"-".repeat(uriColumnWidth - 1) + " " +
|
|
163
|
-
"-".repeat(uriCnColumnWidth - 1) + " " +
|
|
164
|
-
"-".repeat(nameColumnWidth - 1);
|
|
165
|
-
Ec.waiting(separator);
|
|
166
|
-
|
|
167
|
-
// 数据行
|
|
168
|
-
agents.forEach((agent, index) => {
|
|
169
|
-
const name = `${agent.argName} (${agent.name})`;
|
|
170
|
-
const row = `${(index + 1).toString().padEnd(6)}` +
|
|
171
|
-
`${agent.type.padEnd(typeColumnWidth)}` +
|
|
172
|
-
`${agent.id.padEnd(idColumnWidth).red.bold}` +
|
|
173
|
-
`${(agent.uri || '').padEnd(uriColumnWidth)}` +
|
|
174
|
-
`${(agent.uriCn || '').padEnd(uriCnColumnWidth)}` +
|
|
175
|
-
name.padEnd(nameColumnWidth);
|
|
176
|
-
Ec.waiting(row);
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
Ec.waiting(separator);
|
|
180
|
-
Ec.waiting(`${agents.length + 1}. 退出`);
|
|
181
|
-
|
|
182
|
-
// 获取用户选择
|
|
183
|
-
const answer = await Ec.ask('请选择要使用的 Agent (输入编号): ');
|
|
184
|
-
const selectedIndex = parseInt(answer) - 1;
|
|
185
|
-
|
|
186
|
-
// 验证选择
|
|
187
|
-
if (isNaN(selectedIndex) || selectedIndex < 0 || selectedIndex > agents.length) {
|
|
188
|
-
Ec.error("❌ 无效的选择");
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// 检查是否选择退出
|
|
193
|
-
if (selectedIndex === agents.length) {
|
|
194
|
-
Ec.info("已退出 Agent 选择");
|
|
195
|
-
Ec.askClose();
|
|
196
|
-
process.exit(0);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// 使用选择的 agent
|
|
200
|
-
const selectedAgent = agents[selectedIndex];
|
|
201
|
-
return await _processAgent(selectedAgent);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// 查找指定的 agent
|
|
206
|
-
const agent = agents.find(a => a.argName === agentName);
|
|
207
|
-
if (!agent) {
|
|
208
|
-
Ec.error(`❌ 未找到名为 "${agentName}" 的 Agent`);
|
|
209
|
-
|
|
210
|
-
// 显示所有 agents(表格形式)
|
|
211
|
-
Ec.waiting('可用的 Agents:');
|
|
212
|
-
|
|
213
|
-
// 计算列宽以适应内容
|
|
214
|
-
const typeColumnWidth = 12;
|
|
215
|
-
const idColumnWidth = 20;
|
|
216
|
-
const uriColumnWidth = 35;
|
|
217
|
-
const uriCnColumnWidth = 35;
|
|
218
|
-
const nameColumnWidth = 35;
|
|
219
|
-
|
|
220
|
-
// 表头
|
|
221
|
-
const header = "编号".padEnd(6) +
|
|
222
|
-
"Type".padEnd(typeColumnWidth) +
|
|
223
|
-
"ID".padEnd(idColumnWidth) +
|
|
224
|
-
"URI".padEnd(uriColumnWidth) +
|
|
225
|
-
"URI-CN".padEnd(uriCnColumnWidth) +
|
|
226
|
-
"Name".padEnd(nameColumnWidth);
|
|
227
|
-
Ec.waiting(header);
|
|
228
|
-
|
|
229
|
-
// 分隔线
|
|
230
|
-
const separator = "-".repeat(5) + " " +
|
|
231
|
-
"-".repeat(typeColumnWidth - 1) + " " +
|
|
232
|
-
"-".repeat(idColumnWidth - 1) + " " +
|
|
233
|
-
"-".repeat(uriColumnWidth - 1) + " " +
|
|
234
|
-
"-".repeat(uriCnColumnWidth - 1) + " " +
|
|
235
|
-
"-".repeat(nameColumnWidth - 1);
|
|
236
|
-
Ec.waiting(separator);
|
|
237
|
-
|
|
238
|
-
// 数据行
|
|
239
|
-
agents.forEach((agent, index) => {
|
|
240
|
-
const name = `${agent.argName} (${agent.name})`;
|
|
241
|
-
const row = `${(index + 1).toString().padEnd(6)}` +
|
|
242
|
-
`${agent.type.padEnd(typeColumnWidth)}` +
|
|
243
|
-
`${agent.id.padEnd(idColumnWidth).red.bold}` +
|
|
244
|
-
`${(agent.uri || '').padEnd(uriColumnWidth)}` +
|
|
245
|
-
`${(agent.uriCn || '').padEnd(uriCnColumnWidth)}` +
|
|
246
|
-
name.padEnd(nameColumnWidth);
|
|
247
|
-
Ec.waiting(row);
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
Ec.waiting(separator);
|
|
251
|
-
Ec.waiting(`${agents.length + 1}. 退出`);
|
|
252
|
-
Ec.askClose();
|
|
253
|
-
process.exit(1);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// 处理选中的 agent
|
|
257
|
-
await _processAgent(agent);
|
|
258
|
-
|
|
259
|
-
} catch (error) {
|
|
260
|
-
Ec.error(`❌ 执行过程中发生错误: ${error.message}`);
|
|
261
|
-
Ec.askClose();
|
|
262
|
-
process.exit(1);
|
|
263
|
-
}
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* 处理选中的 agent
|
|
268
|
-
* @param {Object} agent 选中的 agent 对象
|
|
269
|
-
*/
|
|
270
|
-
const _processAgent = async (agent) => {
|
|
271
|
-
try {
|
|
272
|
-
// 构建模板路径
|
|
273
|
-
const templatePath = path.resolve(__dirname, `../_template/PROMPT/${agent.type}/${agent.id}.ejs`);
|
|
274
|
-
|
|
275
|
-
// 检查模板文件是否存在
|
|
276
|
-
if (!fs.existsSync(templatePath)) {
|
|
277
|
-
Ec.error(`❌ 未找到模板文件: ${templatePath}`);
|
|
278
|
-
Ec.askClose();
|
|
279
|
-
process.exit(1);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// 提取模板内容
|
|
283
|
-
const content = await _extractTemplateContent(templatePath);
|
|
284
|
-
|
|
285
|
-
// 复制到剪贴板
|
|
286
|
-
await _copyToClipboard(content);
|
|
287
|
-
|
|
288
|
-
// 显示警告提示
|
|
289
|
-
Ec.waiting('');
|
|
290
|
-
Ec.warn('⚠️ 为了获得最佳效果,请在您的 IDE 工具窗口中选择对应的 Agent 来配合使用此提示词');
|
|
291
|
-
|
|
292
|
-
Ec.askClose();
|
|
293
|
-
process.exit(0);
|
|
294
|
-
} catch (error) {
|
|
295
|
-
Ec.error(`❌ 处理 Agent 时发生错误: ${error.message}`);
|
|
296
|
-
Ec.askClose();
|
|
297
|
-
process.exit(1);
|
|
298
|
-
}
|
|
299
|
-
};
|