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,133 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const Ec = require('../epic');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 从 Markdown 文件中提取编号信息
|
|
7
|
-
* @param {string} filePath 文件路径
|
|
8
|
-
* @param {string} dirName 目录名(用于从目录名中提取编号)
|
|
9
|
-
* @returns {string} 编号信息或 'N/A'
|
|
10
|
-
*/
|
|
11
|
-
const _extractId = (filePath, dirName) => {
|
|
12
|
-
try {
|
|
13
|
-
// 首先尝试从目录名中提取编号(MXX格式)
|
|
14
|
-
const dirMatch = dirName.match(/^M\d{2}/);
|
|
15
|
-
if (dirMatch) {
|
|
16
|
-
return dirMatch[0];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (!fs.existsSync(filePath)) {
|
|
20
|
-
return 'N/A';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
24
|
-
|
|
25
|
-
// 首先尝试查找 <!-- CODE: XXX --> 格式
|
|
26
|
-
const codeMatch = content.match(/<!--\s*CODE:\s*(.+?)\s*-->/i);
|
|
27
|
-
if (codeMatch) {
|
|
28
|
-
return codeMatch[1].trim();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// 然后尝试从文件内容的标题中提取编号(MXX格式)
|
|
32
|
-
const lines = content.split('\n');
|
|
33
|
-
for (const line of lines) {
|
|
34
|
-
// 匹配 # MXX 格式的标题(M后跟两位数字)
|
|
35
|
-
const titleMatch = line.match(/^#\s+(M\d{2})/);
|
|
36
|
-
if (titleMatch) {
|
|
37
|
-
return titleMatch[1];
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// 如果没有找到CODE格式,尝试查找 "- 编号:" 格式
|
|
42
|
-
for (const line of lines) {
|
|
43
|
-
const idMatch = line.match(/-\s*编号[::]\s*(.+)/);
|
|
44
|
-
if (idMatch) {
|
|
45
|
-
return idMatch[1].trim();
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return 'N/A';
|
|
50
|
-
} catch (error) {
|
|
51
|
-
return 'N/A';
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* 统计任务数量
|
|
57
|
-
* @param {string} tasksFile 任务文件路径
|
|
58
|
-
* @returns {number} 任务数量
|
|
59
|
-
*/
|
|
60
|
-
const _countTasks = (tasksFile) => {
|
|
61
|
-
try {
|
|
62
|
-
if (!fs.existsSync(tasksFile)) {
|
|
63
|
-
return 0;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const content = fs.readFileSync(tasksFile, 'utf8');
|
|
67
|
-
const lines = content.split('\n');
|
|
68
|
-
let count = 0;
|
|
69
|
-
|
|
70
|
-
// 匹配任务列表格式:
|
|
71
|
-
// - [ ] 任务描述
|
|
72
|
-
// - [xxx] 任务描述
|
|
73
|
-
for (const line of lines) {
|
|
74
|
-
if (line.trim().match(/^-\s*\[.*\]/)) {
|
|
75
|
-
count++;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return count;
|
|
80
|
-
} catch (error) {
|
|
81
|
-
return 0;
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
module.exports = (options) => {
|
|
86
|
-
try {
|
|
87
|
-
// 构建路径
|
|
88
|
-
const projectDir = process.cwd();
|
|
89
|
-
const changesDir = path.join(projectDir, 'specification', 'changes');
|
|
90
|
-
|
|
91
|
-
// 检查 changes 目录是否存在
|
|
92
|
-
if (!fs.existsSync(changesDir)) {
|
|
93
|
-
Ec.waiting('未找到任何需求');
|
|
94
|
-
process.exit(0);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// 读取所有需求目录
|
|
98
|
-
const requirements = fs.readdirSync(changesDir)
|
|
99
|
-
.filter(item => {
|
|
100
|
-
const itemPath = path.join(changesDir, item);
|
|
101
|
-
return fs.statSync(itemPath).isDirectory();
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
if (requirements.length === 0) {
|
|
105
|
-
Ec.waiting('未找到任何需求');
|
|
106
|
-
process.exit(0);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// 显示表头(调整列宽和间距)
|
|
110
|
-
const header = '编号'.padEnd(12) + '名称'.padEnd(32) + '任务数量';
|
|
111
|
-
Ec.waiting(header);
|
|
112
|
-
|
|
113
|
-
// 显示分隔线
|
|
114
|
-
const separator = '-'.repeat(10) + ' ' + '-'.repeat(30) + ' ' + '-'.repeat(8);
|
|
115
|
-
Ec.waiting(separator);
|
|
116
|
-
|
|
117
|
-
// 显示需求列表
|
|
118
|
-
requirements.forEach((requirement) => {
|
|
119
|
-
const proposalFile = path.join(changesDir, requirement, 'proposal.md');
|
|
120
|
-
const tasksFile = path.join(changesDir, requirement, 'tasks.md');
|
|
121
|
-
const id = _extractId(proposalFile, requirement);
|
|
122
|
-
const taskCount = _countTasks(tasksFile);
|
|
123
|
-
const line = id.padEnd(12) + requirement.padEnd(32) + String(taskCount);
|
|
124
|
-
Ec.waiting(line);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
Ec.info(`✅ 共找到 ${requirements.length} 个需求`);
|
|
128
|
-
process.exit(0);
|
|
129
|
-
} catch (error) {
|
|
130
|
-
Ec.error(`❌ 执行过程中发生错误: ${error.message}`);
|
|
131
|
-
process.exit(1);
|
|
132
|
-
}
|
|
133
|
-
};
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const Ec = require('../epic');
|
|
4
|
-
const fsAsync = require('fs').promises;
|
|
5
|
-
const { outCopy } = require('../epic/momo.fn.out');
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* 读取模板文件并提取 <!-- BEGIN --> 到 <!-- END --> 之间的内容
|
|
9
|
-
* @param {string} templatePath 模板文件路径
|
|
10
|
-
* @returns {Promise<string>} 提取的模板内容
|
|
11
|
-
*/
|
|
12
|
-
const _readTemplate = async (templatePath) => {
|
|
13
|
-
try {
|
|
14
|
-
const content = await fsAsync.readFile(templatePath, 'utf8');
|
|
15
|
-
const lines = content.split('\n');
|
|
16
|
-
let beginIndex = -1;
|
|
17
|
-
let endIndex = -1;
|
|
18
|
-
|
|
19
|
-
for (let i = 0; i < lines.length; i++) {
|
|
20
|
-
if (lines[i].includes('<!-- BEGIN -->')) {
|
|
21
|
-
beginIndex = i;
|
|
22
|
-
} else if (lines[i].includes('<!-- END -->')) {
|
|
23
|
-
endIndex = i;
|
|
24
|
-
break;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (beginIndex !== -1 && endIndex !== -1) {
|
|
29
|
-
// 提取 BEGIN 和 END 之间的内容(不包括 BEGIN 和 END 行)
|
|
30
|
-
return lines.slice(beginIndex + 1, endIndex).join('\n');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return content;
|
|
34
|
-
} catch (error) {
|
|
35
|
-
Ec.waiting(`读取模板文件失败: ${error.message}`);
|
|
36
|
-
throw error;
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* 渲染 EJS 模板
|
|
42
|
-
* @param {string} template 模板内容
|
|
43
|
-
* @param {Object} data 数据对象
|
|
44
|
-
* @returns {string} 渲染后的内容
|
|
45
|
-
*/
|
|
46
|
-
const _renderTemplate = (template, data) => {
|
|
47
|
-
let result = template;
|
|
48
|
-
for (const key in data) {
|
|
49
|
-
if (data.hasOwnProperty(key)) {
|
|
50
|
-
const regex = new RegExp(`<%=\\s*${key}\\s*%>`, 'g');
|
|
51
|
-
result = result.replace(regex, data[key]);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return result;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* 检查需求目录是否存在
|
|
59
|
-
* @param {string} changesDir changes目录路径
|
|
60
|
-
* @param {string} requirementName 需求名称
|
|
61
|
-
* @returns {boolean} 是否存在
|
|
62
|
-
*/
|
|
63
|
-
const _isRequirementExists = (changesDir, requirementName) => {
|
|
64
|
-
const requirementDir = path.join(changesDir, requirementName);
|
|
65
|
-
return fs.existsSync(requirementDir);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* 将内容复制到剪贴板(去除换行符)
|
|
70
|
-
* @param {string} content 要复制的内容
|
|
71
|
-
* @param {string} requirementName 需求名称
|
|
72
|
-
*/
|
|
73
|
-
const _copyToClipboard = async (content, requirementName) => {
|
|
74
|
-
try {
|
|
75
|
-
// 去除换行符,将内容合并为一行
|
|
76
|
-
const contentWithoutNewlines = content.replace(/\r?\n|\r/g, ' ');
|
|
77
|
-
|
|
78
|
-
// 使用统一的剪贴板函数
|
|
79
|
-
await outCopy(contentWithoutNewlines);
|
|
80
|
-
Ec.waiting('✅ 计划提示词已复制到剪贴板');
|
|
81
|
-
|
|
82
|
-
// 保存内容到 .working 目录
|
|
83
|
-
const workingDir = path.resolve(process.cwd(), '.working');
|
|
84
|
-
const fileName = `REQ-${requirementName}.txt`;
|
|
85
|
-
const filePath = path.join(workingDir, fileName);
|
|
86
|
-
|
|
87
|
-
// 确保 .working 目录存在
|
|
88
|
-
if (!fs.existsSync(workingDir)) {
|
|
89
|
-
await fsAsync.mkdir(workingDir, { recursive: true });
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// 写入文件
|
|
93
|
-
await fsAsync.writeFile(filePath, content);
|
|
94
|
-
Ec.waiting(`📄 计划提示词已保存到文件: ${filePath}`);
|
|
95
|
-
} catch (error) {
|
|
96
|
-
Ec.waiting(`复制到剪贴板失败: ${error.message}`);
|
|
97
|
-
// 提供备选方案
|
|
98
|
-
Ec.waiting('计划提示词内容:');
|
|
99
|
-
console.log(content);
|
|
100
|
-
if (process.platform === 'win32') {
|
|
101
|
-
Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
module.exports = async (options) => {
|
|
107
|
-
// 参数提取
|
|
108
|
-
const parsed = Ec.parseArgument(options);
|
|
109
|
-
|
|
110
|
-
// 获取需求名称
|
|
111
|
-
const requirementName = parsed.name || parsed.n;
|
|
112
|
-
|
|
113
|
-
// 验证参数
|
|
114
|
-
if (!requirementName) {
|
|
115
|
-
Ec.error("❌ 请提供需求名称 (-n, --name)");
|
|
116
|
-
process.exit(1);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// 检查需求名称是否包含点号(扩展名)
|
|
120
|
-
if (requirementName.includes('.')) {
|
|
121
|
-
Ec.error("❌ 需求名称不能包含点号(.),以防止与文件扩展名混淆");
|
|
122
|
-
process.exit(1);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// 在 Windows 上检查非法字符
|
|
126
|
-
if (process.platform === 'win32') {
|
|
127
|
-
const illegalChars = /[<>:"/\\|?*\x00-\x1F]/g;
|
|
128
|
-
if (illegalChars.test(requirementName)) {
|
|
129
|
-
Ec.error(`❌ 需求名称 "${requirementName}" 包含非法字符,请避免使用以下字符: < > : " / \\ | ? * 以及控制字符`);
|
|
130
|
-
process.exit(1);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
Ec.waiting(`准备生成需求计划: ${requirementName}`);
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
// 获取项目路径
|
|
138
|
-
const projectDir = process.cwd();
|
|
139
|
-
const changesDir = path.join(projectDir, 'specification', 'changes');
|
|
140
|
-
const promptTemplatePath = path.join(__dirname, '../_template/PROMPT/plan.md.ejs');
|
|
141
|
-
|
|
142
|
-
// 检查需求是否存在
|
|
143
|
-
if (!_isRequirementExists(changesDir, requirementName)) {
|
|
144
|
-
Ec.error(`❌ 需求 "${requirementName}" 不存在`);
|
|
145
|
-
process.exit(1);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// 读取并处理提示词模板
|
|
149
|
-
const templateContent = await _readTemplate(promptTemplatePath);
|
|
150
|
-
const renderedContent = _renderTemplate(templateContent, { NAME: requirementName });
|
|
151
|
-
|
|
152
|
-
// 将提示词复制到剪贴板
|
|
153
|
-
await _copyToClipboard(renderedContent, requirementName);
|
|
154
|
-
|
|
155
|
-
Ec.info(`✅ 成功生成需求 "${requirementName}" 的计划提示词`);
|
|
156
|
-
process.exit(0);
|
|
157
|
-
} catch (error) {
|
|
158
|
-
Ec.error(`❌ 执行过程中发生错误: ${error.message}`);
|
|
159
|
-
if (process.platform === 'win32') {
|
|
160
|
-
Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
|
|
161
|
-
}
|
|
162
|
-
process.exit(1);
|
|
163
|
-
}
|
|
164
|
-
};
|
|
@@ -1,313 +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
|
-
mkdir: util.promisify(fs.mkdir),
|
|
9
|
-
copyFile: util.promisify(fs.copyFile),
|
|
10
|
-
readdir: util.promisify(fs.readdir),
|
|
11
|
-
stat: util.promisify(fs.stat),
|
|
12
|
-
readFile: util.promisify(fs.readFile),
|
|
13
|
-
writeFile: util.promisify(fs.writeFile)
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* 获取 refer.json 配置文件路径
|
|
18
|
-
* @param {string} baseDir 项目根目录
|
|
19
|
-
* @returns {string} refer.json 文件路径
|
|
20
|
-
*/
|
|
21
|
-
const _getReferConfigPath = (baseDir) => {
|
|
22
|
-
return path.resolve(baseDir, '.momo', 'advanced', 'refer.json');
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* 获取模板中的 refer.json 配置文件路径
|
|
27
|
-
* @returns {string} 模板中 refer.json 文件路径
|
|
28
|
-
*/
|
|
29
|
-
const _getTemplateReferConfigPath = () => {
|
|
30
|
-
return path.resolve(__dirname, '../_template/LAIN/.momo/advanced/refer.json');
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* 获取默认的 refer 配置
|
|
35
|
-
* @returns {Object} 默认配置对象
|
|
36
|
-
*/
|
|
37
|
-
const _getDefaultReferConfig = () => {
|
|
38
|
-
// 从模板文件读取默认配置
|
|
39
|
-
const templateConfigPath = _getTemplateReferConfigPath();
|
|
40
|
-
if (fs.existsSync(templateConfigPath)) {
|
|
41
|
-
const content = fs.readFileSync(templateConfigPath, 'utf8');
|
|
42
|
-
return JSON.parse(content);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// 如果模板文件不存在,返回硬编码的默认配置
|
|
46
|
-
return {
|
|
47
|
-
maven: {
|
|
48
|
-
file: 'pom.xml',
|
|
49
|
-
target: 'reference/maven'
|
|
50
|
-
},
|
|
51
|
-
gradle: {
|
|
52
|
-
file: 'build.gradle',
|
|
53
|
-
target: 'reference/gradle'
|
|
54
|
-
},
|
|
55
|
-
npm: {
|
|
56
|
-
file: 'package.json',
|
|
57
|
-
target: 'reference/npm'
|
|
58
|
-
},
|
|
59
|
-
rust: {
|
|
60
|
-
file: 'Cargo.toml',
|
|
61
|
-
target: 'reference/rust'
|
|
62
|
-
},
|
|
63
|
-
go: {
|
|
64
|
-
file: 'go.mod',
|
|
65
|
-
target: 'reference/go'
|
|
66
|
-
},
|
|
67
|
-
ruby: {
|
|
68
|
-
file: 'Gemfile',
|
|
69
|
-
target: 'reference/ruby'
|
|
70
|
-
},
|
|
71
|
-
python: {
|
|
72
|
-
file: 'requirements.txt',
|
|
73
|
-
target: 'reference/python'
|
|
74
|
-
},
|
|
75
|
-
php: {
|
|
76
|
-
file: 'composer.json',
|
|
77
|
-
target: 'reference/php'
|
|
78
|
-
},
|
|
79
|
-
dotnet: {
|
|
80
|
-
file: 'project.csproj',
|
|
81
|
-
target: 'reference/dotnet'
|
|
82
|
-
},
|
|
83
|
-
cmake: {
|
|
84
|
-
file: 'CMakeLists.txt',
|
|
85
|
-
target: 'reference/cmake'
|
|
86
|
-
},
|
|
87
|
-
make: {
|
|
88
|
-
file: 'Makefile',
|
|
89
|
-
target: 'reference/make'
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* 读取或创建 refer.json 配置文件
|
|
96
|
-
* @param {string} configPath 配置文件路径
|
|
97
|
-
* @returns {Promise<Object>} 配置对象
|
|
98
|
-
*/
|
|
99
|
-
const _readOrCreateReferConfig = async (configPath) => {
|
|
100
|
-
// 确保 .momo/advanced 目录存在
|
|
101
|
-
const configDir = path.dirname(configPath);
|
|
102
|
-
if (!fs.existsSync(configDir)) {
|
|
103
|
-
await fsAsync.mkdir(configDir, { recursive: true });
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const defaultConfig = _getDefaultReferConfig();
|
|
107
|
-
|
|
108
|
-
// 如果配置文件不存在,创建默认配置
|
|
109
|
-
if (!fs.existsSync(configPath)) {
|
|
110
|
-
await fsAsync.writeFile(configPath, JSON.stringify(defaultConfig, null, 4));
|
|
111
|
-
Ec.waiting(`创建默认配置文件: ${path.relative(process.cwd(), configPath)}`);
|
|
112
|
-
return defaultConfig;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// 读取现有配置
|
|
116
|
-
try {
|
|
117
|
-
const content = await fsAsync.readFile(configPath, 'utf8');
|
|
118
|
-
const existingConfig = JSON.parse(content);
|
|
119
|
-
|
|
120
|
-
// 检查是否需要更新配置(添加新的项目类型)
|
|
121
|
-
let updated = false;
|
|
122
|
-
for (const [key, value] of Object.entries(defaultConfig)) {
|
|
123
|
-
if (!existingConfig[key]) {
|
|
124
|
-
existingConfig[key] = value;
|
|
125
|
-
updated = true;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// 如果有更新,则写回文件
|
|
130
|
-
if (updated) {
|
|
131
|
-
await fsAsync.writeFile(configPath, JSON.stringify(existingConfig, null, 4));
|
|
132
|
-
Ec.waiting(`更新配置文件: ${path.relative(process.cwd(), configPath)}`);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return existingConfig;
|
|
136
|
-
} catch (error) {
|
|
137
|
-
Ec.error(`配置文件格式错误: ${configPath}`);
|
|
138
|
-
throw error;
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* 显示当前支持的项目类型(表格形式)
|
|
144
|
-
* @param {Object} referConfig 引用配置
|
|
145
|
-
*/
|
|
146
|
-
const _showSupportedTypes = (referConfig) => {
|
|
147
|
-
Ec.waiting("当前支持的项目类型:");
|
|
148
|
-
|
|
149
|
-
// 定义列宽
|
|
150
|
-
const columnWidth = 24;
|
|
151
|
-
|
|
152
|
-
// 表头
|
|
153
|
-
const header = "Type".padEnd(columnWidth) +
|
|
154
|
-
"ID File".padEnd(columnWidth) +
|
|
155
|
-
"Store Path";
|
|
156
|
-
Ec.waiting(header);
|
|
157
|
-
|
|
158
|
-
// 分隔线
|
|
159
|
-
const separator = "-".repeat(columnWidth - 1) + " " +
|
|
160
|
-
"-".repeat(columnWidth - 1) + " " +
|
|
161
|
-
"-".repeat(columnWidth - 1);
|
|
162
|
-
Ec.waiting(separator);
|
|
163
|
-
|
|
164
|
-
// 数据行
|
|
165
|
-
for (const [type, config] of Object.entries(referConfig)) {
|
|
166
|
-
const row = type.padEnd(columnWidth) +
|
|
167
|
-
config.file.padEnd(columnWidth) +
|
|
168
|
-
config.target;
|
|
169
|
-
Ec.waiting(row);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
Ec.info("使用方法: momo project -s \"项目路径\"");
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* 检查路径是否为目录
|
|
177
|
-
* @param {string} sourcePath 路径
|
|
178
|
-
* @returns {Promise<boolean>} 是否为目录
|
|
179
|
-
*/
|
|
180
|
-
const _isDirectory = async (sourcePath) => {
|
|
181
|
-
try {
|
|
182
|
-
const stats = await fsAsync.stat(sourcePath);
|
|
183
|
-
return stats.isDirectory();
|
|
184
|
-
} catch (error) {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* 检查目录中是否存在指定文件
|
|
191
|
-
* @param {string} sourceDir 源目录
|
|
192
|
-
* @param {string} fileName 文件名
|
|
193
|
-
* @returns {Promise<boolean>} 是否存在文件
|
|
194
|
-
*/
|
|
195
|
-
const _hasFile = async (sourceDir, fileName) => {
|
|
196
|
-
try {
|
|
197
|
-
const filePath = path.join(sourceDir, fileName);
|
|
198
|
-
const stats = await fsAsync.stat(filePath);
|
|
199
|
-
return stats.isFile();
|
|
200
|
-
} catch (error) {
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* 递归拷贝目录
|
|
207
|
-
* @param {string} src 源目录
|
|
208
|
-
* @param {string} dest 目标目录
|
|
209
|
-
*/
|
|
210
|
-
const _copyDir = async (src, dest) => {
|
|
211
|
-
const entries = await fsAsync.readdir(src, { withFileTypes: true });
|
|
212
|
-
await fsAsync.mkdir(dest, { recursive: true });
|
|
213
|
-
|
|
214
|
-
for (let entry of entries) {
|
|
215
|
-
const srcPath = path.join(src, entry.name);
|
|
216
|
-
const destPath = path.join(dest, entry.name);
|
|
217
|
-
|
|
218
|
-
if (entry.isDirectory()) {
|
|
219
|
-
await _copyDir(srcPath, destPath);
|
|
220
|
-
} else {
|
|
221
|
-
await fsAsync.copyFile(srcPath, destPath);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* 处理项目引用
|
|
228
|
-
* @param {string} sourcePath 源项目路径
|
|
229
|
-
* @param {Object} referConfig 引用配置
|
|
230
|
-
* @param {string} baseDir 项目根目录
|
|
231
|
-
*/
|
|
232
|
-
const _processProjectReference = async (sourcePath, referConfig, baseDir) => {
|
|
233
|
-
// 检查源路径是否为目录
|
|
234
|
-
if (!(await _isDirectory(sourcePath))) {
|
|
235
|
-
Ec.error(`源路径必须是一个目录: ${sourcePath}`);
|
|
236
|
-
process.exit(1);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
Ec.waiting(`检查项目: ${sourcePath}`);
|
|
240
|
-
|
|
241
|
-
// 遍历配置中的每种项目类型,但排除java类型
|
|
242
|
-
for (const [type, config] of Object.entries(referConfig)) {
|
|
243
|
-
// 跳过java类型处理
|
|
244
|
-
if (type === 'java') {
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const { file, target } = config;
|
|
249
|
-
|
|
250
|
-
// 检查源目录中是否存在指定文件
|
|
251
|
-
if (await _hasFile(sourcePath, file)) {
|
|
252
|
-
Ec.waiting(`检测到 ${type} 项目 (${file})`);
|
|
253
|
-
|
|
254
|
-
// 构建目标目录路径
|
|
255
|
-
const targetDir = path.resolve(baseDir, target);
|
|
256
|
-
|
|
257
|
-
// 确保目标目录存在
|
|
258
|
-
if (!fs.existsSync(targetDir)) {
|
|
259
|
-
Ec.waiting(`创建目录: ${target}`);
|
|
260
|
-
await fsAsync.mkdir(targetDir, { recursive: true });
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// 生成目标子目录名称(使用源目录名称)
|
|
264
|
-
const sourceDirName = path.basename(sourcePath);
|
|
265
|
-
const finalTargetDir = path.join(targetDir, sourceDirName);
|
|
266
|
-
|
|
267
|
-
// 拷贝项目
|
|
268
|
-
try {
|
|
269
|
-
Ec.waiting(`拷贝项目到: ${path.join(target, sourceDirName)}`);
|
|
270
|
-
await _copyDir(sourcePath, finalTargetDir);
|
|
271
|
-
Ec.waiting(`✅ ${type} 项目引用成功`);
|
|
272
|
-
} catch (error) {
|
|
273
|
-
Ec.error(`❌ 拷贝项目失败: ${error.message}`);
|
|
274
|
-
throw error;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
module.exports = async (options) => {
|
|
281
|
-
// 参数提取
|
|
282
|
-
const parsed = Ec.parseArgument(options);
|
|
283
|
-
const sourcePath = parsed.source;
|
|
284
|
-
|
|
285
|
-
try {
|
|
286
|
-
// 获取项目根目录
|
|
287
|
-
const baseDir = process.cwd();
|
|
288
|
-
|
|
289
|
-
// 获取 refer.json 配置文件路径
|
|
290
|
-
const configPath = _getReferConfigPath(baseDir);
|
|
291
|
-
|
|
292
|
-
// 读取或创建配置文件
|
|
293
|
-
const referConfig = await _readOrCreateReferConfig(configPath);
|
|
294
|
-
|
|
295
|
-
// 如果没有提供 source 参数,则显示支持的项目类型
|
|
296
|
-
if (!sourcePath) {
|
|
297
|
-
_showSupportedTypes(referConfig);
|
|
298
|
-
Ec.askClose();
|
|
299
|
-
process.exit(0);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// 处理项目引用
|
|
303
|
-
await _processProjectReference(sourcePath, referConfig, baseDir);
|
|
304
|
-
|
|
305
|
-
Ec.info("项目引用处理完成!");
|
|
306
|
-
Ec.askClose();
|
|
307
|
-
process.exit(0);
|
|
308
|
-
} catch (error) {
|
|
309
|
-
Ec.error(`执行过程中发生错误: ${error.message}`);
|
|
310
|
-
Ec.askClose();
|
|
311
|
-
process.exit(1);
|
|
312
|
-
}
|
|
313
|
-
};
|