momo-ai 1.0.15 → 1.0.18
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/run.md.ejs +1 -0
- package/src/commander/repo.json +2 -2
- package/src/executor/executeConsole.js +50 -23
- package/src/executor/executePush.js +65 -2
- package/src/executor/executeRepo.js +2 -2
- package/src/executor/executeRun.js +104 -6
- package/src/executor/executeTasks.js +73 -3
package/package.json
CHANGED
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');
|
|
@@ -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
|
|
|
@@ -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) {
|
|
@@ -115,16 +115,36 @@ 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
|
|
|
121
122
|
// 检查任务文件是否存在
|
|
122
123
|
if (fs.existsSync(taskPath)) {
|
|
124
|
+
// 提取任务标题
|
|
125
|
+
const title = _extractTitleFromMarkdown(taskPath);
|
|
123
126
|
taskInstances.push({
|
|
124
127
|
name: taskName,
|
|
125
128
|
path: taskPath,
|
|
126
129
|
requirement: requirement,
|
|
127
|
-
relativePath: path.relative(process.cwd(), taskPath)
|
|
130
|
+
relativePath: path.relative(process.cwd(), taskPath),
|
|
131
|
+
title: title
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 如果 .md 文件不存在,检查 .done 文件
|
|
136
|
+
const doneFile = `${taskName}.done`;
|
|
137
|
+
const donePath = path.resolve(tasksDir, doneFile);
|
|
138
|
+
|
|
139
|
+
if (!fs.existsSync(taskPath) && fs.existsSync(donePath)) {
|
|
140
|
+
// 提取任务标题(从 .done 文件中)
|
|
141
|
+
const title = _extractTitleFromMarkdown(donePath);
|
|
142
|
+
taskInstances.push({
|
|
143
|
+
name: taskName,
|
|
144
|
+
path: donePath,
|
|
145
|
+
requirement: requirement,
|
|
146
|
+
relativePath: path.relative(process.cwd(), donePath),
|
|
147
|
+
title: title
|
|
128
148
|
});
|
|
129
149
|
}
|
|
130
150
|
}
|
|
@@ -316,6 +336,48 @@ const _extractTitleFromMarkdown = (filePath) => {
|
|
|
316
336
|
}
|
|
317
337
|
};
|
|
318
338
|
|
|
339
|
+
/**
|
|
340
|
+
* 保存任务分派记录
|
|
341
|
+
* @param {string} taskName 任务名称
|
|
342
|
+
* @param {string} workspaceName 工作空间名称
|
|
343
|
+
* @param {string} actorName Actor名称
|
|
344
|
+
* @param {string} requirementName 需求名称
|
|
345
|
+
*/
|
|
346
|
+
const _saveAssignmentRecord = async (taskName, workspaceName, actorName, requirementName) => {
|
|
347
|
+
try {
|
|
348
|
+
// 确保 .activities 目录存在
|
|
349
|
+
const activitiesDir = path.resolve(process.cwd(), '.activities');
|
|
350
|
+
if (!fs.existsSync(activitiesDir)) {
|
|
351
|
+
await fsAsync.mkdir(activitiesDir, { recursive: true });
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// 创建文件名:{任务编号}-develop-xx-时间戳.txt
|
|
355
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '_').slice(0, -5);
|
|
356
|
+
// 只使用工作空间名称,而不是完整路径
|
|
357
|
+
const workspaceDirName = path.basename(workspaceName);
|
|
358
|
+
const fileName = `${taskName}-${workspaceDirName}-${timestamp}.txt`;
|
|
359
|
+
const filePath = path.join(activitiesDir, fileName);
|
|
360
|
+
|
|
361
|
+
// 写入分派记录
|
|
362
|
+
const recordContent = `Task Assignment Record
|
|
363
|
+
=====================
|
|
364
|
+
|
|
365
|
+
Task Name: ${taskName}
|
|
366
|
+
Workspace: ${workspaceDirName}
|
|
367
|
+
Actor: ${actorName}
|
|
368
|
+
Requirement: ${requirementName}
|
|
369
|
+
Assigned At: ${new Date().toISOString()}
|
|
370
|
+
|
|
371
|
+
This task has been assigned to workspace ${workspaceDirName} for actor ${actorName}.
|
|
372
|
+
`;
|
|
373
|
+
|
|
374
|
+
await fsAsync.writeFile(filePath, recordContent);
|
|
375
|
+
Ec.waiting(`[Momo AI] 任务分派记录已保存: ${filePath}`);
|
|
376
|
+
} catch (error) {
|
|
377
|
+
Ec.waiting(`⚠️ 无法保存任务分派记录: ${error.message}`);
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
|
|
319
381
|
module.exports = async (options) => {
|
|
320
382
|
// 参数提取
|
|
321
383
|
const parsed = Ec.parseArgument(options);
|
|
@@ -367,16 +429,26 @@ module.exports = async (options) => {
|
|
|
367
429
|
const coloredName = task.name.cyan;
|
|
368
430
|
Ec.waiting(`${index + 1}. [${coloredStatus}] ${task.display} (${coloredName})`);
|
|
369
431
|
});
|
|
432
|
+
|
|
433
|
+
// 添加退出选项
|
|
434
|
+
Ec.waiting(`${allTasks.length + 1}. 退出`);
|
|
370
435
|
|
|
371
436
|
// 获取用户选择
|
|
372
437
|
const answer = await Ec.ask('请输入选项编号: ');
|
|
373
438
|
const selectedIndex = parseInt(answer) - 1;
|
|
374
439
|
|
|
375
440
|
// 验证选择
|
|
376
|
-
if (isNaN(selectedIndex) || selectedIndex < 0 || selectedIndex
|
|
441
|
+
if (isNaN(selectedIndex) || selectedIndex < 0 || selectedIndex > allTasks.length) {
|
|
377
442
|
Ec.error('❌ 无效的选择');
|
|
378
443
|
continue;
|
|
379
444
|
}
|
|
445
|
+
|
|
446
|
+
// 检查是否选择退出
|
|
447
|
+
if (selectedIndex === allTasks.length) {
|
|
448
|
+
Ec.info("已退出任务选择");
|
|
449
|
+
Ec.askClose();
|
|
450
|
+
process.exit(0);
|
|
451
|
+
}
|
|
380
452
|
|
|
381
453
|
// 检查选中的任务是否正在进行中
|
|
382
454
|
const tasksDir = path.resolve(process.cwd(), 'specification', 'changes', allTasks[selectedIndex].requirement, 'tasks');
|
|
@@ -386,6 +458,13 @@ module.exports = async (options) => {
|
|
|
386
458
|
Ec.error('❌ 不能选择正在进行中的任务,请重新选择');
|
|
387
459
|
continue;
|
|
388
460
|
}
|
|
461
|
+
|
|
462
|
+
// 检查选中的任务是否已完成
|
|
463
|
+
if (status === '已完成') {
|
|
464
|
+
Ec.info(`✅ 任务 ${allTasks[selectedIndex].name} 已经完成,不用分派`);
|
|
465
|
+
Ec.askClose();
|
|
466
|
+
process.exit(0);
|
|
467
|
+
}
|
|
389
468
|
|
|
390
469
|
// 直接使用用户选择的任务,跳过重复检查
|
|
391
470
|
selectedTask = allTasks[selectedIndex];
|
|
@@ -451,6 +530,13 @@ module.exports = async (options) => {
|
|
|
451
530
|
Ec.error('❌ 不能选择正在进行中的任务,请重新选择');
|
|
452
531
|
continue;
|
|
453
532
|
}
|
|
533
|
+
|
|
534
|
+
// 检查选中的任务是否已完成
|
|
535
|
+
if (status === '已完成') {
|
|
536
|
+
Ec.info(`✅ 任务 ${taskInstances[selectedIndex].name} 已经完成,不用分派`);
|
|
537
|
+
Ec.askClose();
|
|
538
|
+
process.exit(0);
|
|
539
|
+
}
|
|
454
540
|
|
|
455
541
|
selectedTask = taskInstances[selectedIndex];
|
|
456
542
|
break;
|
|
@@ -466,6 +552,13 @@ module.exports = async (options) => {
|
|
|
466
552
|
process.exit(1);
|
|
467
553
|
}
|
|
468
554
|
|
|
555
|
+
// 检查任务是否已完成
|
|
556
|
+
if (status === '已完成') {
|
|
557
|
+
Ec.info(`✅ 任务 ${taskName} 已经完成,不用分派`);
|
|
558
|
+
Ec.askClose();
|
|
559
|
+
process.exit(0);
|
|
560
|
+
}
|
|
561
|
+
|
|
469
562
|
selectedTask = taskInstances[0];
|
|
470
563
|
}
|
|
471
564
|
|
|
@@ -498,6 +591,11 @@ module.exports = async (options) => {
|
|
|
498
591
|
workspaceLockPath = _createWorkspaceLock(selectedWorkspace.name, selectedTask.name, actorName);
|
|
499
592
|
}
|
|
500
593
|
|
|
594
|
+
// 保存任务分派记录
|
|
595
|
+
if (selectedWorkspace) {
|
|
596
|
+
await _saveAssignmentRecord(selectedTask.name, selectedWorkspace.name, actorName, requirementName);
|
|
597
|
+
}
|
|
598
|
+
|
|
501
599
|
// 读取模板文件并填充参数,然后拷贝到剪贴板
|
|
502
600
|
const templatePath = path.resolve(__dirname, '../_template/PROMPT/run.md.ejs');
|
|
503
601
|
|
|
@@ -512,15 +610,15 @@ module.exports = async (options) => {
|
|
|
512
610
|
});
|
|
513
611
|
|
|
514
612
|
// 打印额外信息
|
|
515
|
-
Ec.waiting(
|
|
516
|
-
Ec.waiting(
|
|
517
|
-
Ec.waiting('
|
|
613
|
+
Ec.waiting(`执行任务: ${selectedTask.name} - ${selectedTask.title || '未知任务'}`);
|
|
614
|
+
Ec.waiting(`使用工作空间: ${selectedWorkspace.path}`);
|
|
615
|
+
Ec.waiting('提示词内容:');
|
|
518
616
|
Ec.waiting('--------------------------------------------------');
|
|
519
617
|
|
|
520
618
|
// 按行打印提示词内容,保持原有格式
|
|
521
619
|
const lines = renderedContent.split('\n');
|
|
522
620
|
lines.forEach(line => {
|
|
523
|
-
Ec.waiting(
|
|
621
|
+
Ec.waiting(line);
|
|
524
622
|
});
|
|
525
623
|
|
|
526
624
|
Ec.waiting('--------------------------------------------------');
|
|
@@ -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
|
// 执行剪切板任务(使用第一个任务)
|