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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "momo-ai",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "Rachel Momo ( OpenSpec )",
5
5
  "main": "src/momo.js",
6
6
  "bin": {
@@ -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. 有多少模块在于你的模块的整体设计,记得模块内部业务要形成完整闭环。
@@ -8,4 +8,5 @@
8
8
  3. 任务文件名统一成 MXX-TNNN.md,其中 NN 是任务编号,NNN 是任务编号,编号长度不足用 0 填充。
9
9
  4. 每个任务文件中要写出任务实施过程的详细步骤。
10
10
  5. 系统相关的数据模型参考 specification/data-model.md 文件。
11
+ 4. 更新 specification/changes/MXX-模块/tasks.md 文件时,每个任务必须使用 `- [ ]` 的前缀做任务标记,后边跟上任务编号和标题。
11
12
  <!-- END -->
@@ -9,8 +9,8 @@
9
9
  "description": "当前项目的远程 Git 仓库地址!"
10
10
  },
11
11
  {
12
- "name": "i",
13
- "alias": "instance",
12
+ "name": "instance",
13
+ "alias": "i",
14
14
  "description": "需要 Clone 的远程仓库的实例副本数量!",
15
15
  "default": 10
16
16
  }
@@ -4,65 +4,92 @@
4
4
 
5
5
  const readline = require('readline');
6
6
  const colors = require('colors');
7
- const Ec = require('../epic');
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
- Ec.executeHeader("Rachel Momo / SDD");
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(' exit - 退出控制台'.white);
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
- console.log('');
54
- console.log('帮助信息:'.bold.brightYellow);
55
- console.log(' help - 显示此帮助信息'.white);
56
- console.log(' exit - 退出控制台'.white);
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
- console.log('');
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
- // 如果没有找到CODE格式,尝试查找 "- 编号:" 格式
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
- // 匹配任务格式,例如: - [ ] [任务1简要描述](tasks/M01-T001-01.md)
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
- Ec.waiting('编号\t\t名称\t\t\t任务数量');
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
- Ec.waiting(`${id}\t\t${requirement}\t\t\t${taskCount}`);
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 || parsed.a;
180
- const instanceCount = parsed.i || parsed.instance || 10; // 默认值为10
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
- const fileName = `${taskName}-${workspaceName}-${timestamp}.txt`;
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: ${workspaceName}
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 ${workspaceName} for actor ${actorName}.
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 >= allTasks.length) {
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(`[Momo AI] 执行任务: ${selectedTask.name} - ${selectedTask.title}`);
596
- Ec.waiting(`[Momo AI] 使用工作空间: ${selectedWorkspace.path}`);
597
- Ec.waiting('[Momo AI] 提示词内容:');
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(`[Momo AI] ${line}`);
621
+ Ec.waiting(line);
604
622
  });
605
623
 
606
624
  Ec.waiting('--------------------------------------------------');