momo-ai 1.0.15 → 1.0.17
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
|
@@ -21,6 +21,44 @@ const _isGitRepo = async (dirPath) => {
|
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* 检查是否有未合并的文件
|
|
26
|
+
* @param {string} dirPath 目录路径
|
|
27
|
+
* @returns {Promise<boolean>} 是否有未合并的文件
|
|
28
|
+
*/
|
|
29
|
+
const _hasUnmergedFiles = async (dirPath) => {
|
|
30
|
+
try {
|
|
31
|
+
const { stdout } = await execAsync('git diff --name-only --diff-filter=U', { cwd: dirPath });
|
|
32
|
+
return stdout.trim() !== '';
|
|
33
|
+
} catch (error) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 执行 git merge 命令合并更改
|
|
40
|
+
* @param {string} dirPath 目录路径
|
|
41
|
+
*/
|
|
42
|
+
const _mergeChanges = async (dirPath) => {
|
|
43
|
+
try {
|
|
44
|
+
Ec.waiting(`正在合并更改: ${dirPath}`);
|
|
45
|
+
const { stdout, stderr } = await execAsync('git merge --no-edit', { cwd: dirPath });
|
|
46
|
+
|
|
47
|
+
if (stdout) {
|
|
48
|
+
Ec.waiting(`输出: ${stdout.trim()}`);
|
|
49
|
+
}
|
|
50
|
+
if (stderr) {
|
|
51
|
+
Ec.waiting(`进度: ${stderr.trim()}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
Ec.waiting(`✅ 成功合并更改: ${dirPath}`);
|
|
55
|
+
return true;
|
|
56
|
+
} catch (error) {
|
|
57
|
+
Ec.error(`❌ 合并更改失败 ${dirPath}: ${error.message}`);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
24
62
|
/**
|
|
25
63
|
* 从远程拉取最新代码
|
|
26
64
|
* @param {string} dirPath 目录路径
|
|
@@ -40,8 +78,33 @@ const _pullRepo = async (dirPath) => {
|
|
|
40
78
|
Ec.waiting(`✅ 成功拉取代码: ${dirPath}`);
|
|
41
79
|
return true;
|
|
42
80
|
} catch (error) {
|
|
43
|
-
|
|
44
|
-
|
|
81
|
+
// 检查是否是分支分歧错误
|
|
82
|
+
if (error.message.includes('Need to specify how to reconcile divergent branches')) {
|
|
83
|
+
try {
|
|
84
|
+
Ec.waiting(`检测到分支分歧错误,正在配置 pull.rebase=false: ${dirPath}`);
|
|
85
|
+
await execAsync('git config pull.rebase false', { cwd: dirPath });
|
|
86
|
+
Ec.waiting(`已设置 pull.rebase=false,重新尝试拉取代码: ${dirPath}`);
|
|
87
|
+
|
|
88
|
+
// 重新执行 pull 操作
|
|
89
|
+
const { stdout, stderr } = await execAsync('git pull', { cwd: dirPath });
|
|
90
|
+
|
|
91
|
+
if (stdout) {
|
|
92
|
+
Ec.waiting(`输出: ${stdout.trim()}`);
|
|
93
|
+
}
|
|
94
|
+
if (stderr) {
|
|
95
|
+
Ec.waiting(`进度: ${stderr.trim()}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
Ec.waiting(`✅ 成功拉取代码: ${dirPath}`);
|
|
99
|
+
return true;
|
|
100
|
+
} catch (retryError) {
|
|
101
|
+
Ec.error(`❌ 重新拉取代码失败 ${dirPath}: ${retryError.message}`);
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
Ec.error(`❌ 拉取代码失败 ${dirPath}: ${error.message}`);
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
45
108
|
}
|
|
46
109
|
};
|
|
47
110
|
|
|
@@ -115,6 +115,7 @@ const _findTaskInstances = (taskName) => {
|
|
|
115
115
|
|
|
116
116
|
// 检查 tasks 目录是否存在
|
|
117
117
|
if (fs.existsSync(tasksDir) && fs.statSync(tasksDir).isDirectory()) {
|
|
118
|
+
// 检查 .md 文件
|
|
118
119
|
const taskFile = `${taskName}.md`;
|
|
119
120
|
const taskPath = path.resolve(tasksDir, taskFile);
|
|
120
121
|
|
|
@@ -127,6 +128,19 @@ const _findTaskInstances = (taskName) => {
|
|
|
127
128
|
relativePath: path.relative(process.cwd(), taskPath)
|
|
128
129
|
});
|
|
129
130
|
}
|
|
131
|
+
|
|
132
|
+
// 如果 .md 文件不存在,检查 .done 文件
|
|
133
|
+
const doneFile = `${taskName}.done`;
|
|
134
|
+
const donePath = path.resolve(tasksDir, doneFile);
|
|
135
|
+
|
|
136
|
+
if (!fs.existsSync(taskPath) && fs.existsSync(donePath)) {
|
|
137
|
+
taskInstances.push({
|
|
138
|
+
name: taskName,
|
|
139
|
+
path: donePath,
|
|
140
|
+
requirement: requirement,
|
|
141
|
+
relativePath: path.relative(process.cwd(), donePath)
|
|
142
|
+
});
|
|
143
|
+
}
|
|
130
144
|
}
|
|
131
145
|
});
|
|
132
146
|
|
|
@@ -316,6 +330,46 @@ const _extractTitleFromMarkdown = (filePath) => {
|
|
|
316
330
|
}
|
|
317
331
|
};
|
|
318
332
|
|
|
333
|
+
/**
|
|
334
|
+
* 保存任务分派记录
|
|
335
|
+
* @param {string} taskName 任务名称
|
|
336
|
+
* @param {string} workspaceName 工作空间名称
|
|
337
|
+
* @param {string} actorName Actor名称
|
|
338
|
+
* @param {string} requirementName 需求名称
|
|
339
|
+
*/
|
|
340
|
+
const _saveAssignmentRecord = async (taskName, workspaceName, actorName, requirementName) => {
|
|
341
|
+
try {
|
|
342
|
+
// 确保 .activities 目录存在
|
|
343
|
+
const activitiesDir = path.resolve(process.cwd(), '.activities');
|
|
344
|
+
if (!fs.existsSync(activitiesDir)) {
|
|
345
|
+
await fsAsync.mkdir(activitiesDir, { recursive: true });
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// 创建文件名:{任务编号}-develop-xx-时间戳.txt
|
|
349
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '_').slice(0, -5);
|
|
350
|
+
const fileName = `${taskName}-${workspaceName}-${timestamp}.txt`;
|
|
351
|
+
const filePath = path.join(activitiesDir, fileName);
|
|
352
|
+
|
|
353
|
+
// 写入分派记录
|
|
354
|
+
const recordContent = `Task Assignment Record
|
|
355
|
+
=====================
|
|
356
|
+
|
|
357
|
+
Task Name: ${taskName}
|
|
358
|
+
Workspace: ${workspaceName}
|
|
359
|
+
Actor: ${actorName}
|
|
360
|
+
Requirement: ${requirementName}
|
|
361
|
+
Assigned At: ${new Date().toISOString()}
|
|
362
|
+
|
|
363
|
+
This task has been assigned to workspace ${workspaceName} for actor ${actorName}.
|
|
364
|
+
`;
|
|
365
|
+
|
|
366
|
+
await fsAsync.writeFile(filePath, recordContent);
|
|
367
|
+
Ec.waiting(`[Momo AI] 任务分派记录已保存: ${filePath}`);
|
|
368
|
+
} catch (error) {
|
|
369
|
+
Ec.waiting(`⚠️ 无法保存任务分派记录: ${error.message}`);
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
|
|
319
373
|
module.exports = async (options) => {
|
|
320
374
|
// 参数提取
|
|
321
375
|
const parsed = Ec.parseArgument(options);
|
|
@@ -386,6 +440,13 @@ module.exports = async (options) => {
|
|
|
386
440
|
Ec.error('❌ 不能选择正在进行中的任务,请重新选择');
|
|
387
441
|
continue;
|
|
388
442
|
}
|
|
443
|
+
|
|
444
|
+
// 检查选中的任务是否已完成
|
|
445
|
+
if (status === '已完成') {
|
|
446
|
+
Ec.info(`✅ 任务 ${allTasks[selectedIndex].name} 已经完成,不用分派`);
|
|
447
|
+
Ec.askClose();
|
|
448
|
+
process.exit(0);
|
|
449
|
+
}
|
|
389
450
|
|
|
390
451
|
// 直接使用用户选择的任务,跳过重复检查
|
|
391
452
|
selectedTask = allTasks[selectedIndex];
|
|
@@ -451,6 +512,13 @@ module.exports = async (options) => {
|
|
|
451
512
|
Ec.error('❌ 不能选择正在进行中的任务,请重新选择');
|
|
452
513
|
continue;
|
|
453
514
|
}
|
|
515
|
+
|
|
516
|
+
// 检查选中的任务是否已完成
|
|
517
|
+
if (status === '已完成') {
|
|
518
|
+
Ec.info(`✅ 任务 ${taskInstances[selectedIndex].name} 已经完成,不用分派`);
|
|
519
|
+
Ec.askClose();
|
|
520
|
+
process.exit(0);
|
|
521
|
+
}
|
|
454
522
|
|
|
455
523
|
selectedTask = taskInstances[selectedIndex];
|
|
456
524
|
break;
|
|
@@ -466,6 +534,13 @@ module.exports = async (options) => {
|
|
|
466
534
|
process.exit(1);
|
|
467
535
|
}
|
|
468
536
|
|
|
537
|
+
// 检查任务是否已完成
|
|
538
|
+
if (status === '已完成') {
|
|
539
|
+
Ec.info(`✅ 任务 ${taskName} 已经完成,不用分派`);
|
|
540
|
+
Ec.askClose();
|
|
541
|
+
process.exit(0);
|
|
542
|
+
}
|
|
543
|
+
|
|
469
544
|
selectedTask = taskInstances[0];
|
|
470
545
|
}
|
|
471
546
|
|
|
@@ -498,6 +573,11 @@ module.exports = async (options) => {
|
|
|
498
573
|
workspaceLockPath = _createWorkspaceLock(selectedWorkspace.name, selectedTask.name, actorName);
|
|
499
574
|
}
|
|
500
575
|
|
|
576
|
+
// 保存任务分派记录
|
|
577
|
+
if (selectedWorkspace) {
|
|
578
|
+
await _saveAssignmentRecord(selectedTask.name, selectedWorkspace.name, actorName, requirementName);
|
|
579
|
+
}
|
|
580
|
+
|
|
501
581
|
// 读取模板文件并填充参数,然后拷贝到剪贴板
|
|
502
582
|
const templatePath = path.resolve(__dirname, '../_template/PROMPT/run.md.ejs');
|
|
503
583
|
|
|
@@ -164,6 +164,7 @@ module.exports = async (options) => {
|
|
|
164
164
|
|
|
165
165
|
// 检查 tasks 目录是否存在
|
|
166
166
|
if (fs.existsSync(tasksDir) && fs.statSync(tasksDir).isDirectory()) {
|
|
167
|
+
// 检查 .md 文件
|
|
167
168
|
const taskFile = `${taskName}.md`;
|
|
168
169
|
const taskPath = path.resolve(tasksDir, taskFile);
|
|
169
170
|
|
|
@@ -176,6 +177,19 @@ module.exports = async (options) => {
|
|
|
176
177
|
requirement: requirement
|
|
177
178
|
});
|
|
178
179
|
}
|
|
180
|
+
|
|
181
|
+
// 检查 .done 文件(如果 .md 文件不存在)
|
|
182
|
+
const doneFile = `${taskName}.done`;
|
|
183
|
+
const donePath = path.resolve(tasksDir, doneFile);
|
|
184
|
+
|
|
185
|
+
if (!fs.existsSync(taskPath) && fs.existsSync(donePath)) {
|
|
186
|
+
const relativePath = path.relative(process.cwd(), donePath);
|
|
187
|
+
taskInstances.push({
|
|
188
|
+
name: taskName,
|
|
189
|
+
path: relativePath,
|
|
190
|
+
requirement: requirement
|
|
191
|
+
});
|
|
192
|
+
}
|
|
179
193
|
}
|
|
180
194
|
});
|
|
181
195
|
|
|
@@ -217,11 +231,17 @@ module.exports = async (options) => {
|
|
|
217
231
|
// 检查 tasks 目录是否存在
|
|
218
232
|
if (fs.existsSync(tasksDir) && fs.statSync(tasksDir).isDirectory()) {
|
|
219
233
|
// 获取所有 .md 文件
|
|
220
|
-
const
|
|
234
|
+
const mdFiles = fs.readdirSync(tasksDir).filter(file =>
|
|
221
235
|
file.endsWith('.md')
|
|
222
236
|
);
|
|
223
237
|
|
|
224
|
-
|
|
238
|
+
// 获取所有 .done 文件
|
|
239
|
+
const doneFiles = fs.readdirSync(tasksDir).filter(file =>
|
|
240
|
+
file.endsWith('.done')
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
// 处理 .md 文件
|
|
244
|
+
mdFiles.forEach(taskFile => {
|
|
225
245
|
const name = path.basename(taskFile, '.md');
|
|
226
246
|
const taskPath = path.relative(process.cwd(), path.resolve(tasksDir, taskFile));
|
|
227
247
|
const title = _extractTitleFromMarkdown(path.resolve(tasksDir, taskFile));
|
|
@@ -251,9 +271,58 @@ module.exports = async (options) => {
|
|
|
251
271
|
status: coloredStatus
|
|
252
272
|
});
|
|
253
273
|
});
|
|
274
|
+
|
|
275
|
+
// 处理 .done 文件(但没有对应的 .md 文件)
|
|
276
|
+
doneFiles.forEach(doneFile => {
|
|
277
|
+
const name = path.basename(doneFile, '.done');
|
|
278
|
+
// 检查是否已经有对应的 .md 文件在任务列表中
|
|
279
|
+
const hasMdFile = mdFiles.some(mdFile =>
|
|
280
|
+
path.basename(mdFile, '.md') === name
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
// 如果没有对应的 .md 文件,则添加到任务列表
|
|
284
|
+
if (!hasMdFile) {
|
|
285
|
+
const donePath = path.relative(process.cwd(), path.resolve(tasksDir, doneFile));
|
|
286
|
+
// 尝试从 .done 文件中提取标题,如果失败则使用文件名
|
|
287
|
+
let title = _extractTitleFromMarkdown(path.resolve(tasksDir, `${name}.md`));
|
|
288
|
+
// 如果从 .md 文件提取失败,则尝试从 .done 文件提取
|
|
289
|
+
if (title === name) {
|
|
290
|
+
title = _extractTitleFromMarkdown(path.resolve(tasksDir, doneFile));
|
|
291
|
+
}
|
|
292
|
+
// 使用标准的状态检查函数
|
|
293
|
+
const status = _checkTaskStatus(name, tasksDir);
|
|
294
|
+
// 为状态添加颜色代码
|
|
295
|
+
let coloredStatus;
|
|
296
|
+
switch (status) {
|
|
297
|
+
case '进行中':
|
|
298
|
+
coloredStatus = status.blue;
|
|
299
|
+
break;
|
|
300
|
+
case '已完成':
|
|
301
|
+
coloredStatus = status.green;
|
|
302
|
+
break;
|
|
303
|
+
case '未开始':
|
|
304
|
+
coloredStatus = status.red;
|
|
305
|
+
break;
|
|
306
|
+
default:
|
|
307
|
+
coloredStatus = status;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
tasks.push({
|
|
311
|
+
name: name,
|
|
312
|
+
title: title,
|
|
313
|
+
path: donePath,
|
|
314
|
+
requirement: requirement,
|
|
315
|
+
status: coloredStatus
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
254
320
|
}
|
|
255
321
|
});
|
|
256
322
|
|
|
323
|
+
// 按任务名称排序
|
|
324
|
+
tasks.sort((a, b) => a.name.localeCompare(b.name));
|
|
325
|
+
|
|
257
326
|
if (tasks.length === 0) {
|
|
258
327
|
Ec.waiting("🔍 未找到任何任务");
|
|
259
328
|
// 即使未找到任务也执行剪切板任务(使用默认值)
|
|
@@ -269,7 +338,8 @@ module.exports = async (options) => {
|
|
|
269
338
|
const paddedIndex = String(index + 1).padStart(3, '0');
|
|
270
339
|
const coloredName = task.name.cyan;
|
|
271
340
|
const coloredPath = task.path.yellow; // 使用黄色高亮路径
|
|
272
|
-
|
|
341
|
+
|
|
342
|
+
Ec.waiting(`${paddedIndex}. ${coloredName} [${task.status}] | ${task.title} / ${coloredPath}`);
|
|
273
343
|
});
|
|
274
344
|
|
|
275
345
|
// 执行剪切板任务(使用第一个任务)
|