momo-ai 1.0.18 → 1.0.20

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.18",
3
+ "version": "1.0.20",
4
4
  "description": "Rachel Momo ( OpenSpec )",
5
5
  "main": "src/momo.js",
6
6
  "bin": {
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "领域建模师",
3
3
  "id": "momo-datamodel",
4
- "uri": "https://s.trae.ai/a/a9dcaa"
4
+ "uri": "https://s.trae.ai/a/a9dcaa",
5
+ "uri-cn": "https://s.trae.com.cn/a/5345ed"
5
6
  }
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "模块需求分析师",
3
3
  "id": "momo-module-req",
4
- "uri": "https://s.trae.ai/a/c4fc46"
4
+ "uri": "https://s.trae.ai/a/c4fc46",
5
+ "uri-cn": "https://s.trae.com.cn/a/700528"
5
6
  }
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "系统需求分析师",
3
3
  "id": "momo-system-req",
4
- "uri": "https://s.trae.ai/a/d0c6b9"
4
+ "uri": "https://s.trae.ai/a/d0c6b9",
5
+ "uri-cn": "https://s.trae.com.cn/a/dd9aef"
5
6
  }
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "任务规划师",
3
3
  "id": "momo-task",
4
- "uri": "https://s.trae.ai/a/1fdc4e"
4
+ "uri": "https://s.trae.ai/a/1fdc4e",
5
+ "uri-cn": "https://s.trae.com.cn/a/ab84c5"
5
6
  }
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "页面规划师",
3
3
  "id": "momo-taskplan",
4
- "uri": "https://s.trae.ai/a/0447bd"
4
+ "uri": "https://s.trae.ai/a/0447bd",
5
+ "uri-cn": "https://s.trae.com.cn/a/534cf3"
5
6
  }
@@ -35,10 +35,6 @@
35
35
  "file": "project.csproj",
36
36
  "target": "reference/dotnet"
37
37
  },
38
- "java": {
39
- "file": "pom.xml",
40
- "target": "reference/java"
41
- },
42
38
  "cmake": {
43
39
  "file": "CMakeLists.txt",
44
40
  "target": "reference/cmake"
@@ -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 -->
@@ -45,6 +45,7 @@ const _getAgents = () => {
45
45
  id: agentInfo.id,
46
46
  name: agentInfo.name,
47
47
  uri: agentInfo.uri,
48
+ uriCn: agentInfo['uri-cn'], // 添加CN链接
48
49
  filePath: filePath
49
50
  });
50
51
  } catch (error) {
@@ -137,9 +138,45 @@ module.exports = async (options) => {
137
138
  if (!agentName) {
138
139
  while (true) {
139
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
+ // 数据行
140
168
  agents.forEach((agent, index) => {
141
- Ec.waiting(`${index + 1}. ${agent.argName.red.bold} (${agent.name}), id = ${agent.id}, uri = ${agent.uri}`);
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);
142
177
  });
178
+
179
+ Ec.waiting(separator);
143
180
  Ec.waiting(`${agents.length + 1}. 退出`);
144
181
 
145
182
  // 获取用户选择
@@ -169,10 +206,48 @@ module.exports = async (options) => {
169
206
  const agent = agents.find(a => a.argName === agentName);
170
207
  if (!agent) {
171
208
  Ec.error(`❌ 未找到名为 "${agentName}" 的 Agent`);
209
+
210
+ // 显示所有 agents(表格形式)
172
211
  Ec.waiting('可用的 Agents:');
173
- agents.forEach((a, index) => {
174
- Ec.waiting(`${index + 1}. ${a.argName.red.bold} (${a.name}), id = ${a.id}, uri = ${a.uri}`);
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);
175
248
  });
249
+
250
+ Ec.waiting(separator);
176
251
  Ec.waiting(`${agents.length + 1}. 退出`);
177
252
  Ec.askClose();
178
253
  process.exit(1);
@@ -38,6 +38,7 @@ const _getAgents = () => {
38
38
  name: agentInfo.name,
39
39
  id: agentInfo.id,
40
40
  uri: agentInfo.uri,
41
+ uriCn: agentInfo['uri-cn'], // 添加CN链接
41
42
  filePath: filePath
42
43
  });
43
44
  } catch (error) {
@@ -60,35 +61,39 @@ const _showAgents = (agents) => {
60
61
  }
61
62
 
62
63
  // 计算列宽
63
- const columnWidth = 20;
64
- const uriColumnWidth = 30;
64
+ const typeColumnWidth = 12;
65
+ const idColumnWidth = 20;
66
+ const uriColumnWidth = 35;
67
+ const uriCnColumnWidth = 35;
68
+ const nameColumnWidth = 35;
65
69
 
66
70
  // 表头
67
71
  const header = "编号".padEnd(6) +
68
- "Arg Name".padEnd(columnWidth) +
69
- "Type".padEnd(columnWidth) +
70
- "ID".padEnd(columnWidth) +
72
+ "Type".padEnd(typeColumnWidth) +
73
+ "ID".padEnd(idColumnWidth) +
71
74
  "URI".padEnd(uriColumnWidth) +
72
- "Name";
75
+ "URI-CN".padEnd(uriCnColumnWidth) +
76
+ "Name".padEnd(nameColumnWidth);
73
77
  Ec.waiting(header);
74
78
 
75
79
  // 分隔线
76
80
  const separator = "-".repeat(5) + " " +
77
- "-".repeat(columnWidth - 1) + " " +
78
- "-".repeat(columnWidth - 1) + " " +
79
- "-".repeat(columnWidth - 1) + " " +
81
+ "-".repeat(typeColumnWidth - 1) + " " +
82
+ "-".repeat(idColumnWidth - 1) + " " +
80
83
  "-".repeat(uriColumnWidth - 1) + " " +
81
- "-".repeat(columnWidth - 1);
84
+ "-".repeat(uriCnColumnWidth - 1) + " " +
85
+ "-".repeat(nameColumnWidth - 1);
82
86
  Ec.waiting(separator);
83
87
 
84
88
  // 数据行
85
89
  agents.forEach((agent, index) => {
90
+ const name = `${agent.argName} (${agent.name})`;
86
91
  const row = `${(index + 1).toString().padEnd(6)}` +
87
- `${agent.argName.padEnd(columnWidth).red.bold}` +
88
- `${agent.type.padEnd(columnWidth)}` +
89
- `${agent.id.padEnd(columnWidth)}` +
90
- `${agent.uri.padEnd(uriColumnWidth)}` +
91
- agent.name;
92
+ `${agent.type.padEnd(typeColumnWidth)}` +
93
+ `${agent.id.padEnd(idColumnWidth).red.bold}` +
94
+ `${(agent.uri || '').padEnd(uriColumnWidth)}` +
95
+ `${(agent.uriCn || '').padEnd(uriCnColumnWidth)}` +
96
+ name.padEnd(nameColumnWidth);
92
97
  Ec.waiting(row);
93
98
  });
94
99
 
@@ -157,9 +162,19 @@ const _interactiveSelect = async (agents) => {
157
162
  continue;
158
163
  }
159
164
 
160
- // 打开选中的 agent
165
+ // 让用户选择打开国际版还是中文版链接
161
166
  const selectedAgent = agents[selectedIndex];
162
- _openInBrowser(selectedAgent.uri, selectedAgent.type);
167
+ let uriToOpen = selectedAgent.uri;
168
+
169
+ if (selectedAgent.uriCn) {
170
+ const choice = await Ec.ask('选择打开链接版本: 1. 国际版 2. 中文版 (默认国际版): ');
171
+ if (choice === '2') {
172
+ uriToOpen = selectedAgent.uriCn;
173
+ }
174
+ }
175
+
176
+ // 打开选中的 agent
177
+ _openInBrowser(uriToOpen, selectedAgent.type);
163
178
 
164
179
  // 等待一段时间让用户看到结果
165
180
  await new Promise(resolve => setTimeout(resolve, 2000));
@@ -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} 个需求`);
@@ -80,10 +80,6 @@ const _getDefaultReferConfig = () => {
80
80
  file: 'project.csproj',
81
81
  target: 'reference/dotnet'
82
82
  },
83
- java: {
84
- file: 'pom.xml',
85
- target: 'reference/java'
86
- },
87
83
  cmake: {
88
84
  file: 'CMakeLists.txt',
89
85
  target: 'reference/cmake'
@@ -242,8 +238,13 @@ const _processProjectReference = async (sourcePath, referConfig, baseDir) => {
242
238
 
243
239
  Ec.waiting(`检查项目: ${sourcePath}`);
244
240
 
245
- // 遍历配置中的每种项目类型
241
+ // 遍历配置中的每种项目类型,但排除java类型
246
242
  for (const [type, config] of Object.entries(referConfig)) {
243
+ // 跳过java类型处理
244
+ if (type === 'java') {
245
+ continue;
246
+ }
247
+
247
248
  const { file, target } = config;
248
249
 
249
250
  // 检查源目录中是否存在指定文件