momo-ai 1.0.17 → 1.0.19
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/package.json +1 -1
- package/src/_template/PROMPT/trae/momo-module-req.ejs +1 -1
- package/src/_template/PROMPT/trae/momo-task.ejs +1 -0
- package/src/commander/repo.json +2 -2
- package/src/executor/executeConsole.js +50 -23
- package/src/executor/executeList.js +32 -10
- package/src/executor/executeRepo.js +2 -2
- package/src/executor/executeRun.js +28 -10
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
详细解读 specification/requirement.md 需求文档中的模块和每个模块的功能列表,在 specification/changes/ 目录之下创建模块的核心文档,每个模块用 MXX- 前缀,其中 XX 是模块编号。
|
|
5
5
|
|
|
6
6
|
1. 每个模块中包括 tasks/ 目录、任务明细文档存放位置。
|
|
7
|
-
2. 每个模块中包括 proposal.md
|
|
7
|
+
2. 每个模块中包括 proposal.md,此文档中用来存放模块的详细需求,记得头部使用带有需求编号的注释:`<!-- CODE: XXX -->`。
|
|
8
8
|
3. 每个模块中包括 tasks.md,此文档中是描述所有任务的总清单。
|
|
9
9
|
4. 模块所需的数据模型可参考 specification/project-model.md 文档。
|
|
10
10
|
5. 有多少模块在于你的模块的整体设计,记得模块内部业务要形成完整闭环。
|
package/src/commander/repo.json
CHANGED
|
@@ -4,65 +4,92 @@
|
|
|
4
4
|
|
|
5
5
|
const readline = require('readline');
|
|
6
6
|
const colors = require('colors');
|
|
7
|
-
const
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const terminalCommands = require('../terminal');
|
|
10
|
+
|
|
11
|
+
// 设置颜色主题
|
|
12
|
+
colors.setTheme({
|
|
13
|
+
silly: 'rainbow',
|
|
14
|
+
input: 'grey',
|
|
15
|
+
verbose: 'cyan',
|
|
16
|
+
prompt: 'red',
|
|
17
|
+
info: 'green',
|
|
18
|
+
data: 'blue',
|
|
19
|
+
help: 'cyan',
|
|
20
|
+
warn: 'yellow',
|
|
21
|
+
debug: 'magenta',
|
|
22
|
+
error: 'red'
|
|
23
|
+
});
|
|
8
24
|
|
|
9
25
|
// 显示欢迎界面和菜单
|
|
10
26
|
const showMenu = () => {
|
|
11
27
|
// 清屏
|
|
12
28
|
process.stdout.write('\x1Bc');
|
|
13
|
-
|
|
29
|
+
|
|
14
30
|
// 显示标准头部信息
|
|
15
|
-
|
|
16
|
-
|
|
31
|
+
showHeader();
|
|
32
|
+
|
|
17
33
|
// 使用96个字符宽度
|
|
18
34
|
const width = 96;
|
|
19
35
|
const headerBorder = '='.repeat(width).blue;
|
|
20
36
|
const footerBorder = '-'.repeat(width).blue;
|
|
21
|
-
|
|
37
|
+
|
|
22
38
|
console.log('');
|
|
23
39
|
console.log(headerBorder);
|
|
24
40
|
const title = 'Momo AI / Lain Console';
|
|
25
41
|
const padding = ' '.repeat(Math.floor((width - title.length) / 2) - 1);
|
|
26
42
|
console.log(`${padding}${title}`.bold.brightCyan);
|
|
27
43
|
console.log(headerBorder);
|
|
28
|
-
|
|
44
|
+
|
|
29
45
|
console.log('');
|
|
30
46
|
console.log('欢迎使用 Momo AI / Lain 控制台!'.green);
|
|
31
47
|
console.log('这是一个交互式命令行界面。'.yellow);
|
|
32
48
|
console.log('');
|
|
33
|
-
|
|
49
|
+
|
|
34
50
|
console.log('可用命令:'.bold);
|
|
35
51
|
console.log(' help - 显示帮助信息'.white);
|
|
36
|
-
console.log('
|
|
52
|
+
console.log(' llm - 查看大模型配置信息'.white);
|
|
37
53
|
console.log(' quit - 退出控制台'.white);
|
|
38
54
|
console.log('');
|
|
39
|
-
|
|
55
|
+
|
|
40
56
|
console.log('请在提示符后输入命令。'.gray);
|
|
41
57
|
console.log(footerBorder);
|
|
42
58
|
};
|
|
43
59
|
|
|
60
|
+
// 显示标准头部信息
|
|
61
|
+
const showHeader = () => {
|
|
62
|
+
const appInfo = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../../package.json'), 'utf8'));
|
|
63
|
+
|
|
64
|
+
console.log(`[Momo AI]`.green.bold + ` ----------------- Rachel Momo / AI工具项 ------------------`.rainbow);
|
|
65
|
+
console.log(`[Momo AI]`.green.bold + ' 应用名称: '.bold + 'Rachel Momo / SDD');
|
|
66
|
+
console.log(`[Momo AI]`.green.bold + ' 工具主页: '.bold + appInfo.homepage.blue);
|
|
67
|
+
console.log(`[Momo AI]`.green.bold + ` 工具版本: ` + `${appInfo.version}`.red + ' ' + `( Node >= 22.x )`.yellow);
|
|
68
|
+
console.log(`[Momo AI]`.green.bold);
|
|
69
|
+
console.log(`[Momo AI]`.green.bold + ` ----------------- AI 系统启动…… ----------------------------`.rainbow);
|
|
70
|
+
};
|
|
71
|
+
|
|
44
72
|
// 处理用户输入
|
|
45
|
-
const handleInput = (input) => {
|
|
73
|
+
const handleInput = (input, commands) => {
|
|
46
74
|
const command = input.trim().toLowerCase();
|
|
47
|
-
|
|
75
|
+
|
|
76
|
+
// 创建命令上下文
|
|
77
|
+
const context = {
|
|
78
|
+
commands: commands
|
|
79
|
+
};
|
|
80
|
+
|
|
48
81
|
switch (command) {
|
|
49
82
|
case '':
|
|
50
83
|
// 空命令,不处理
|
|
51
84
|
break;
|
|
52
85
|
case 'help':
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
console.log(' quit - 退出控制台'.white);
|
|
58
|
-
console.log('');
|
|
86
|
+
commands.help(context);
|
|
87
|
+
break;
|
|
88
|
+
case 'llm':
|
|
89
|
+
commands.llm(context);
|
|
59
90
|
break;
|
|
60
|
-
case 'exit':
|
|
61
91
|
case 'quit':
|
|
62
|
-
|
|
63
|
-
console.log('感谢使用 Momo AI / Lain 控制台,再见!'.brightGreen);
|
|
64
|
-
console.log('');
|
|
65
|
-
process.exit(0);
|
|
92
|
+
commands.quit(context);
|
|
66
93
|
break;
|
|
67
94
|
default:
|
|
68
95
|
console.log('');
|
|
@@ -97,7 +124,7 @@ const executeConsole = async () => {
|
|
|
97
124
|
|
|
98
125
|
rl.on('line', (line) => {
|
|
99
126
|
const input = line.trim();
|
|
100
|
-
handleInput(input);
|
|
127
|
+
handleInput(input, terminalCommands);
|
|
101
128
|
rl.prompt();
|
|
102
129
|
}).on('close', () => {
|
|
103
130
|
console.log('\n' + '感谢使用 Momo AI / Lain 控制台,再见!'.brightGreen + '\n');
|
|
@@ -5,10 +5,17 @@ const Ec = require('../epic');
|
|
|
5
5
|
/**
|
|
6
6
|
* 从 Markdown 文件中提取编号信息
|
|
7
7
|
* @param {string} filePath 文件路径
|
|
8
|
+
* @param {string} dirName 目录名(用于从目录名中提取编号)
|
|
8
9
|
* @returns {string} 编号信息或 'N/A'
|
|
9
10
|
*/
|
|
10
|
-
const _extractId = (filePath) => {
|
|
11
|
+
const _extractId = (filePath, dirName) => {
|
|
11
12
|
try {
|
|
13
|
+
// 首先尝试从目录名中提取编号(MXX格式)
|
|
14
|
+
const dirMatch = dirName.match(/^M\d{2}/);
|
|
15
|
+
if (dirMatch) {
|
|
16
|
+
return dirMatch[0];
|
|
17
|
+
}
|
|
18
|
+
|
|
12
19
|
if (!fs.existsSync(filePath)) {
|
|
13
20
|
return 'N/A';
|
|
14
21
|
}
|
|
@@ -21,8 +28,17 @@ const _extractId = (filePath) => {
|
|
|
21
28
|
return codeMatch[1].trim();
|
|
22
29
|
}
|
|
23
30
|
|
|
24
|
-
//
|
|
31
|
+
// 然后尝试从文件内容的标题中提取编号(MXX格式)
|
|
25
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格式,尝试查找 "- 编号:" 格式
|
|
26
42
|
for (const line of lines) {
|
|
27
43
|
const idMatch = line.match(/-\s*编号[::]\s*(.+)/);
|
|
28
44
|
if (idMatch) {
|
|
@@ -51,10 +67,11 @@ const _countTasks = (tasksFile) => {
|
|
|
51
67
|
const lines = content.split('\n');
|
|
52
68
|
let count = 0;
|
|
53
69
|
|
|
54
|
-
//
|
|
70
|
+
// 匹配任务列表格式:
|
|
71
|
+
// - [ ] 任务描述
|
|
72
|
+
// - [xxx] 任务描述
|
|
55
73
|
for (const line of lines) {
|
|
56
|
-
|
|
57
|
-
if (line.trim().startsWith('- [') && line.includes('] [') && line.includes('](') && line.includes('tasks/')) {
|
|
74
|
+
if (line.trim().match(/^-\s*\[.*\]/)) {
|
|
58
75
|
count++;
|
|
59
76
|
}
|
|
60
77
|
}
|
|
@@ -89,17 +106,22 @@ module.exports = (options) => {
|
|
|
89
106
|
process.exit(0);
|
|
90
107
|
}
|
|
91
108
|
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
Ec.waiting(
|
|
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);
|
|
95
116
|
|
|
96
117
|
// 显示需求列表
|
|
97
118
|
requirements.forEach((requirement) => {
|
|
98
119
|
const proposalFile = path.join(changesDir, requirement, 'proposal.md');
|
|
99
120
|
const tasksFile = path.join(changesDir, requirement, 'tasks.md');
|
|
100
|
-
const id = _extractId(proposalFile);
|
|
121
|
+
const id = _extractId(proposalFile, requirement);
|
|
101
122
|
const taskCount = _countTasks(tasksFile);
|
|
102
|
-
|
|
123
|
+
const line = id.padEnd(12) + requirement.padEnd(32) + String(taskCount);
|
|
124
|
+
Ec.waiting(line);
|
|
103
125
|
});
|
|
104
126
|
|
|
105
127
|
Ec.info(`✅ 共找到 ${requirements.length} 个需求`);
|
|
@@ -176,8 +176,8 @@ module.exports = async (options) => {
|
|
|
176
176
|
const parsed = Ec.parseArgument(options);
|
|
177
177
|
|
|
178
178
|
// 获取参数
|
|
179
|
-
const repoUrl = parsed.address
|
|
180
|
-
const instanceCount = parsed.
|
|
179
|
+
const repoUrl = parsed.address;
|
|
180
|
+
const instanceCount = parsed.instance;
|
|
181
181
|
|
|
182
182
|
// 验证参数
|
|
183
183
|
if (!repoUrl) {
|
|
@@ -121,11 +121,14 @@ const _findTaskInstances = (taskName) => {
|
|
|
121
121
|
|
|
122
122
|
// 检查任务文件是否存在
|
|
123
123
|
if (fs.existsSync(taskPath)) {
|
|
124
|
+
// 提取任务标题
|
|
125
|
+
const title = _extractTitleFromMarkdown(taskPath);
|
|
124
126
|
taskInstances.push({
|
|
125
127
|
name: taskName,
|
|
126
128
|
path: taskPath,
|
|
127
129
|
requirement: requirement,
|
|
128
|
-
relativePath: path.relative(process.cwd(), taskPath)
|
|
130
|
+
relativePath: path.relative(process.cwd(), taskPath),
|
|
131
|
+
title: title
|
|
129
132
|
});
|
|
130
133
|
}
|
|
131
134
|
|
|
@@ -134,11 +137,14 @@ const _findTaskInstances = (taskName) => {
|
|
|
134
137
|
const donePath = path.resolve(tasksDir, doneFile);
|
|
135
138
|
|
|
136
139
|
if (!fs.existsSync(taskPath) && fs.existsSync(donePath)) {
|
|
140
|
+
// 提取任务标题(从 .done 文件中)
|
|
141
|
+
const title = _extractTitleFromMarkdown(donePath);
|
|
137
142
|
taskInstances.push({
|
|
138
143
|
name: taskName,
|
|
139
144
|
path: donePath,
|
|
140
145
|
requirement: requirement,
|
|
141
|
-
relativePath: path.relative(process.cwd(), donePath)
|
|
146
|
+
relativePath: path.relative(process.cwd(), donePath),
|
|
147
|
+
title: title
|
|
142
148
|
});
|
|
143
149
|
}
|
|
144
150
|
}
|
|
@@ -347,7 +353,9 @@ const _saveAssignmentRecord = async (taskName, workspaceName, actorName, require
|
|
|
347
353
|
|
|
348
354
|
// 创建文件名:{任务编号}-develop-xx-时间戳.txt
|
|
349
355
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '_').slice(0, -5);
|
|
350
|
-
|
|
356
|
+
// 只使用工作空间名称,而不是完整路径
|
|
357
|
+
const workspaceDirName = path.basename(workspaceName);
|
|
358
|
+
const fileName = `${taskName}-${workspaceDirName}-${timestamp}.txt`;
|
|
351
359
|
const filePath = path.join(activitiesDir, fileName);
|
|
352
360
|
|
|
353
361
|
// 写入分派记录
|
|
@@ -355,12 +363,12 @@ const _saveAssignmentRecord = async (taskName, workspaceName, actorName, require
|
|
|
355
363
|
=====================
|
|
356
364
|
|
|
357
365
|
Task Name: ${taskName}
|
|
358
|
-
Workspace: ${
|
|
366
|
+
Workspace: ${workspaceDirName}
|
|
359
367
|
Actor: ${actorName}
|
|
360
368
|
Requirement: ${requirementName}
|
|
361
369
|
Assigned At: ${new Date().toISOString()}
|
|
362
370
|
|
|
363
|
-
This task has been assigned to workspace ${
|
|
371
|
+
This task has been assigned to workspace ${workspaceDirName} for actor ${actorName}.
|
|
364
372
|
`;
|
|
365
373
|
|
|
366
374
|
await fsAsync.writeFile(filePath, recordContent);
|
|
@@ -421,16 +429,26 @@ module.exports = async (options) => {
|
|
|
421
429
|
const coloredName = task.name.cyan;
|
|
422
430
|
Ec.waiting(`${index + 1}. [${coloredStatus}] ${task.display} (${coloredName})`);
|
|
423
431
|
});
|
|
432
|
+
|
|
433
|
+
// 添加退出选项
|
|
434
|
+
Ec.waiting(`${allTasks.length + 1}. 退出`);
|
|
424
435
|
|
|
425
436
|
// 获取用户选择
|
|
426
437
|
const answer = await Ec.ask('请输入选项编号: ');
|
|
427
438
|
const selectedIndex = parseInt(answer) - 1;
|
|
428
439
|
|
|
429
440
|
// 验证选择
|
|
430
|
-
if (isNaN(selectedIndex) || selectedIndex < 0 || selectedIndex
|
|
441
|
+
if (isNaN(selectedIndex) || selectedIndex < 0 || selectedIndex > allTasks.length) {
|
|
431
442
|
Ec.error('❌ 无效的选择');
|
|
432
443
|
continue;
|
|
433
444
|
}
|
|
445
|
+
|
|
446
|
+
// 检查是否选择退出
|
|
447
|
+
if (selectedIndex === allTasks.length) {
|
|
448
|
+
Ec.info("已退出任务选择");
|
|
449
|
+
Ec.askClose();
|
|
450
|
+
process.exit(0);
|
|
451
|
+
}
|
|
434
452
|
|
|
435
453
|
// 检查选中的任务是否正在进行中
|
|
436
454
|
const tasksDir = path.resolve(process.cwd(), 'specification', 'changes', allTasks[selectedIndex].requirement, 'tasks');
|
|
@@ -592,15 +610,15 @@ module.exports = async (options) => {
|
|
|
592
610
|
});
|
|
593
611
|
|
|
594
612
|
// 打印额外信息
|
|
595
|
-
Ec.waiting(
|
|
596
|
-
Ec.waiting(
|
|
597
|
-
Ec.waiting('
|
|
613
|
+
Ec.waiting(`执行任务: ${selectedTask.name} - ${selectedTask.title || '未知任务'}`);
|
|
614
|
+
Ec.waiting(`使用工作空间: ${selectedWorkspace.path}`);
|
|
615
|
+
Ec.waiting('提示词内容:');
|
|
598
616
|
Ec.waiting('--------------------------------------------------');
|
|
599
617
|
|
|
600
618
|
// 按行打印提示词内容,保持原有格式
|
|
601
619
|
const lines = renderedContent.split('\n');
|
|
602
620
|
lines.forEach(line => {
|
|
603
|
-
Ec.waiting(
|
|
621
|
+
Ec.waiting(line);
|
|
604
622
|
});
|
|
605
623
|
|
|
606
624
|
Ec.waiting('--------------------------------------------------');
|