momo-ai 1.0.88 → 1.0.89
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/README.md +2 -2
- package/package.json +1 -1
- package/src/commander/run.json +1 -1
- package/src/commander/task.json +1 -1
- package/src/executor/executeRun.js +29 -120
- package/src/executor/executeTask.js +13 -1
- package/src/index.test.js +267 -0
- package/.obsidian/plugins/.omc/state/idle-notif-cooldown.json +0 -3
- package/.omc/project-memory.json +0 -191
- package/.omc/sessions/2327d169-ea62-4881-a6e6-c88bf089e8b8.json +0 -8
- package/.omc/sessions/24968ee6-c99b-47be-a904-68fcce1b08ba.json +0 -8
- package/.omc/sessions/3158a19c-5915-4c7e-8a1e-83d65c25bb4a.json +0 -8
- package/.omc/sessions/5d9bfa71-77e1-47e9-b3af-8ff9e2d70ce5.json +0 -8
- package/.omc/sessions/7fa8f05b-d348-424b-a9bf-7e84aaacdc29.json +0 -8
- package/.omc/sessions/81b2f444-6a18-4a87-9a51-1492771689e7.json +0 -8
- package/.omc/sessions/d7901fcc-6318-4bc6-ba68-81722b1a4231.json +0 -8
- package/.omc/sessions/e12400d6-5242-491c-b32a-21db817a0d61.json +0 -8
- package/.omc/sessions/ee30b9d7-e57d-48b5-8363-c1d7cc7adae3.json +0 -8
- package/.omc/sessions/f23b8556-e76e-4986-b4ba-fbfc3fec8da6.json +0 -8
- package/.omc/state/mission-state.json +0 -4
package/README.md
CHANGED
|
@@ -69,8 +69,8 @@ momo help
|
|
|
69
69
|
| 命令 | 说明 | 示例 |
|
|
70
70
|
|:---|:---|:---|
|
|
71
71
|
| `momo ask` | 从模板目录中选择提示词并复制到剪切板 | `momo ask` |
|
|
72
|
-
| `momo run` |
|
|
73
|
-
| `momo task` |
|
|
72
|
+
| `momo run` | 从项目根或 `.r2mo` 目录下的 `task/` 中选择任务,生成提示词到剪贴板 | `momo run` |
|
|
73
|
+
| `momo task` | 按项目根或 `.r2mo` 目录下的 `task/thread` 配置对齐任务槽位;thread 缺失时默认 20,满队列时交互选择要转历史的任务 | `momo task` |
|
|
74
74
|
|
|
75
75
|
<hr/>
|
|
76
76
|
|
package/package.json
CHANGED
package/src/commander/run.json
CHANGED
package/src/commander/task.json
CHANGED
|
@@ -2,44 +2,16 @@ const path = require('path');
|
|
|
2
2
|
const fs = require('fs').promises;
|
|
3
3
|
|
|
4
4
|
const Ec = require('../epic');
|
|
5
|
-
const { exists } = require('../utils/momo-file-utils');
|
|
6
5
|
const { playAudio } = require('../utils/momo-audio');
|
|
7
6
|
|
|
8
7
|
const TASK_DIR = '.r2mo/task';
|
|
9
8
|
const TASK_FILE_RE = /^task-(\d+)\.md$/;
|
|
10
|
-
const FOCUS_CONFIG = '.r2mo/focus/rachel-momo.yaml';
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const pomPath = path.join(basePath, 'pom.xml');
|
|
16
|
-
const content = await fs.readFile(pomPath, 'utf8');
|
|
17
|
-
const withoutParent = content.replace(/<parent>[\s\S]*?<\/parent>/gi, '');
|
|
18
|
-
const match = withoutParent.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
19
|
-
return match && match[1] ? match[1].trim() : null;
|
|
20
|
-
} catch {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
/** 当前目录是否为 DPA 父项目根 */
|
|
26
|
-
const _isDpaParent = async (cwd) => {
|
|
27
|
-
const dirname = path.basename(cwd);
|
|
28
|
-
const hasPom = exists(path.join(cwd, 'pom.xml'));
|
|
29
|
-
if (!hasPom || dirname.endsWith('-domain') || dirname.endsWith('-provider') || dirname.endsWith('-api') || dirname.endsWith('-ui')) {
|
|
30
|
-
return false;
|
|
10
|
+
const _resolveTaskDir = (cwd) => {
|
|
11
|
+
if (path.basename(cwd) === '.r2mo') {
|
|
12
|
+
return path.resolve(cwd, 'task');
|
|
31
13
|
}
|
|
32
|
-
|
|
33
|
-
if (!projectName) return false;
|
|
34
|
-
const domainPath = path.join(cwd, `${projectName}-domain`);
|
|
35
|
-
const providerPath = path.join(cwd, `${projectName}-provider`);
|
|
36
|
-
const apiPath = path.join(cwd, `${projectName}-api`);
|
|
37
|
-
const uiPath = path.join(cwd, `${projectName}-ui`);
|
|
38
|
-
const domainExists = await fs.access(domainPath).then(() => true).catch(() => false);
|
|
39
|
-
const providerExists = await fs.access(providerPath).then(() => true).catch(() => false);
|
|
40
|
-
const apiExists = await fs.access(apiPath).then(() => true).catch(() => false);
|
|
41
|
-
const uiExists = await fs.access(uiPath).then(() => true).catch(() => false);
|
|
42
|
-
return domainExists && providerExists && apiExists && uiExists;
|
|
14
|
+
return path.resolve(cwd, TASK_DIR);
|
|
43
15
|
};
|
|
44
16
|
|
|
45
17
|
/** 去掉顶部 YAML 属性笔记,只保留正文(支持 \n 与 \r\n) */
|
|
@@ -76,103 +48,40 @@ const _promptForTask = (relativePath) => `执行任务:读取当前工作目
|
|
|
76
48
|
|
|
77
49
|
module.exports = async () => {
|
|
78
50
|
const cwd = process.cwd();
|
|
79
|
-
const taskDir =
|
|
80
|
-
playAudio('audio/run.ogg');
|
|
51
|
+
const taskDir = _resolveTaskDir(cwd);
|
|
81
52
|
|
|
82
53
|
try {
|
|
83
54
|
require('colors');
|
|
84
55
|
const { selectSingle } = require('../utils/momo-menu');
|
|
56
|
+
const taskFiles = await _listTaskFiles(taskDir);
|
|
57
|
+
if (taskFiles.length === 0) {
|
|
58
|
+
Ec.warn('.r2mo/task 下暂无 task-00X.md 任务文件');
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
85
61
|
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
);
|
|
97
|
-
if (!modeSelected) {
|
|
98
|
-
Ec.warn('已取消');
|
|
99
|
-
process.exit(0);
|
|
100
|
-
}
|
|
101
|
-
modeName = modeSelected.name;
|
|
102
|
-
} else {
|
|
103
|
-
modeName = 'task';
|
|
62
|
+
const menuItems = [];
|
|
63
|
+
for (const name of taskFiles) {
|
|
64
|
+
const taskPath = path.join(taskDir, name);
|
|
65
|
+
let desc = '无标题';
|
|
66
|
+
try {
|
|
67
|
+
const content = await fs.readFile(taskPath, 'utf8');
|
|
68
|
+
const title = _parseTitleFromContent(content);
|
|
69
|
+
if (title) desc = title;
|
|
70
|
+
} catch (_) {}
|
|
71
|
+
menuItems.push({ name, description: desc });
|
|
104
72
|
}
|
|
105
73
|
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (modeSelected.name === 'focus') {
|
|
112
|
-
const configPath = path.join(cwd, FOCUS_CONFIG);
|
|
113
|
-
if (!exists(configPath)) {
|
|
114
|
-
Ec.error('未找到 focus 配置,请先执行 momo focus 生成 .r2mo/focus/rachel-momo.yaml');
|
|
115
|
-
process.exit(1);
|
|
116
|
-
}
|
|
117
|
-
const yaml = require('js-yaml');
|
|
118
|
-
const configContent = await fs.readFile(configPath, 'utf8');
|
|
119
|
-
const config = yaml.load(configContent);
|
|
120
|
-
const taskset = config && config.taskset ? config.taskset : {};
|
|
121
|
-
const items = [];
|
|
122
|
-
if (taskset.backend) items.push({ name: 'backend', description: '后端', relPath: taskset.backend });
|
|
123
|
-
if (taskset.frontend) items.push({ name: 'frontend', description: '前端', relPath: taskset.frontend });
|
|
124
|
-
if (taskset.leader) items.push({ name: 'leader', description: '集体', relPath: taskset.leader });
|
|
125
|
-
if (items.length === 0) {
|
|
126
|
-
Ec.warn('focus 配置中无任务路径,请先执行 momo focus');
|
|
127
|
-
process.exit(0);
|
|
128
|
-
}
|
|
129
|
-
for (const it of items) {
|
|
130
|
-
const full = path.join(cwd, it.relPath);
|
|
131
|
-
if (exists(full)) {
|
|
132
|
-
try {
|
|
133
|
-
const content = await fs.readFile(full, 'utf8');
|
|
134
|
-
const t = _parseTitleFromContent(content);
|
|
135
|
-
if (t) it.description = t;
|
|
136
|
-
} catch (_) {}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
const focusSelected = await selectSingle(items, '选择 focus 任务');
|
|
140
|
-
if (!focusSelected) {
|
|
141
|
-
Ec.warn('已取消');
|
|
142
|
-
process.exit(0);
|
|
143
|
-
}
|
|
144
|
-
taskPath = path.join(cwd, focusSelected.relPath);
|
|
145
|
-
if (!exists(taskPath)) {
|
|
146
|
-
Ec.error('任务文件不存在: ' + focusSelected.relPath);
|
|
147
|
-
process.exit(1);
|
|
148
|
-
}
|
|
149
|
-
displayPath = path.relative(cwd, taskPath).split(path.sep).join('/');
|
|
150
|
-
} else {
|
|
151
|
-
const taskFiles = await _listTaskFiles(taskDir);
|
|
152
|
-
if (taskFiles.length === 0) {
|
|
153
|
-
Ec.warn('.r2mo/task 下暂无 task-00X.md 任务文件');
|
|
154
|
-
process.exit(0);
|
|
155
|
-
}
|
|
156
|
-
const menuItems = [];
|
|
157
|
-
for (const name of taskFiles) {
|
|
158
|
-
const tp = path.join(taskDir, name);
|
|
159
|
-
let desc = '无标题';
|
|
160
|
-
try {
|
|
161
|
-
const content = await fs.readFile(tp, 'utf8');
|
|
162
|
-
const t = _parseTitleFromContent(content);
|
|
163
|
-
if (t) desc = t;
|
|
164
|
-
} catch (_) {}
|
|
165
|
-
menuItems.push({ name, description: desc });
|
|
166
|
-
}
|
|
167
|
-
const selected = await selectSingle(menuItems, '选择要执行的任务');
|
|
168
|
-
if (!selected) {
|
|
169
|
-
Ec.warn('已取消');
|
|
170
|
-
process.exit(0);
|
|
171
|
-
}
|
|
172
|
-
taskPath = path.join(taskDir, selected.name);
|
|
173
|
-
displayPath = path.relative(cwd, taskPath).split(path.sep).join('/');
|
|
74
|
+
const selected = await selectSingle(menuItems, '选择要执行的任务');
|
|
75
|
+
if (!selected) {
|
|
76
|
+
Ec.warn('已取消');
|
|
77
|
+
process.exit(0);
|
|
174
78
|
}
|
|
175
79
|
|
|
80
|
+
playAudio('audio/run.ogg');
|
|
81
|
+
|
|
82
|
+
const taskPath = path.join(taskDir, selected.name);
|
|
83
|
+
const displayPath = path.relative(cwd, taskPath).split(path.sep).join('/');
|
|
84
|
+
|
|
176
85
|
const content = await fs.readFile(taskPath, 'utf8');
|
|
177
86
|
const body = _stripFrontmatter(content);
|
|
178
87
|
|
|
@@ -6,9 +6,17 @@ const { playAudio } = require('../utils/momo-audio');
|
|
|
6
6
|
const TASK_DIR = '.r2mo/task';
|
|
7
7
|
const THREAD_FILE = 'thread';
|
|
8
8
|
const DEFAULT_SLOTS = 20;
|
|
9
|
+
const HIDDEN_HISTORY_DIR = path.join(require('os').homedir(), '.r2mo', '.task-history');
|
|
9
10
|
|
|
10
11
|
const TASK_FILE_RE = /^task-(\d+)\.md$/;
|
|
11
12
|
|
|
13
|
+
const _resolveTaskDir = (cwd) => {
|
|
14
|
+
if (path.basename(cwd) === '.r2mo') {
|
|
15
|
+
return path.resolve(cwd, 'task');
|
|
16
|
+
}
|
|
17
|
+
return path.resolve(cwd, TASK_DIR);
|
|
18
|
+
};
|
|
19
|
+
|
|
12
20
|
/**
|
|
13
21
|
* 槽位数量以 .r2mo/task/thread 为准。
|
|
14
22
|
* 只有 thread 不存在或值非法时,才回落到默认值 20。
|
|
@@ -91,6 +99,10 @@ const _archiveTask = async (taskDir, filename, content, cwd, withAudio) => {
|
|
|
91
99
|
playAudio('audio/task.ogg');
|
|
92
100
|
}
|
|
93
101
|
await fs.writeFile(historyPath, content, 'utf8');
|
|
102
|
+
// 静默副本:写入 ~/.r2mo/.task-history/日期/ 同名文件
|
|
103
|
+
const hiddenDir = path.join(HIDDEN_HISTORY_DIR, dateDir);
|
|
104
|
+
await fs.mkdir(hiddenDir, { recursive: true });
|
|
105
|
+
await fs.writeFile(path.join(hiddenDir, historyFilename), content, 'utf8');
|
|
94
106
|
await fs.unlink(path.join(taskDir, filename));
|
|
95
107
|
Ec.waiting(`已转移到历史: ${filename} → ${path.relative(cwd, historyPath)}`);
|
|
96
108
|
return historyPath;
|
|
@@ -185,7 +197,7 @@ const _selectReplacementSlot = async (slots, taskSlots) => {
|
|
|
185
197
|
module.exports = (options) => {
|
|
186
198
|
try {
|
|
187
199
|
const cwd = process.cwd();
|
|
188
|
-
const taskDir =
|
|
200
|
+
const taskDir = _resolveTaskDir(cwd);
|
|
189
201
|
const title = '任务';
|
|
190
202
|
|
|
191
203
|
(async () => {
|
package/src/index.test.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const assert = require('assert');
|
|
2
2
|
const fs = require('fs').promises;
|
|
3
|
+
const fsSync = require('fs');
|
|
3
4
|
const path = require('path');
|
|
4
5
|
const os = require('os');
|
|
6
|
+
const Module = require('module');
|
|
5
7
|
const { spawnSync } = require('child_process');
|
|
6
8
|
|
|
7
9
|
const MOMO_JS = path.resolve(__dirname, 'momo.js');
|
|
@@ -67,6 +69,14 @@ const _withTempDir = async (fn) => {
|
|
|
67
69
|
}
|
|
68
70
|
};
|
|
69
71
|
|
|
72
|
+
const _withTempR2moDir = async (fn) => {
|
|
73
|
+
await _withTempDir(async (root) => {
|
|
74
|
+
const r2moDir = path.join(root, '.r2mo');
|
|
75
|
+
await fs.mkdir(r2moDir, { recursive: true });
|
|
76
|
+
await fn(root, r2moDir);
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
70
80
|
const testDefaultThreadFallsBackTo20 = async () => {
|
|
71
81
|
await _withTempDir(async (root) => {
|
|
72
82
|
const result = _runTask(root, { ...process.env, PATH: '' });
|
|
@@ -115,10 +125,267 @@ const testShrinkThreadPrunesOverflow = async () => {
|
|
|
115
125
|
});
|
|
116
126
|
};
|
|
117
127
|
|
|
128
|
+
const testRunSkipsFocusModeSelection = async () => {
|
|
129
|
+
const runFile = path.resolve(__dirname, 'executor', 'executeRun.js');
|
|
130
|
+
|
|
131
|
+
await _withTempDir(async (root) => {
|
|
132
|
+
const taskRoot = path.join(root, TASK_DIR);
|
|
133
|
+
await fs.mkdir(taskRoot, { recursive: true });
|
|
134
|
+
await fs.writeFile(path.join(taskRoot, _slotFilename(1)), _taskContent('直接执行任务', '# Body'), 'utf8');
|
|
135
|
+
await fs.writeFile(path.join(root, 'pom.xml'), '<project><artifactId>demo</artifactId></project>', 'utf8');
|
|
136
|
+
await fs.mkdir(path.join(root, 'demo-domain'), { recursive: true });
|
|
137
|
+
await fs.mkdir(path.join(root, 'demo-provider'), { recursive: true });
|
|
138
|
+
await fs.mkdir(path.join(root, 'demo-api'), { recursive: true });
|
|
139
|
+
await fs.mkdir(path.join(root, 'demo-ui'), { recursive: true });
|
|
140
|
+
|
|
141
|
+
const menuTitles = [];
|
|
142
|
+
const originalLoad = Module._load;
|
|
143
|
+
const originalCwd = process.cwd;
|
|
144
|
+
const originalExit = process.exit;
|
|
145
|
+
const originalLog = console.log;
|
|
146
|
+
const originalInfo = console.info;
|
|
147
|
+
const originalWarn = console.warn;
|
|
148
|
+
const originalError = console.error;
|
|
149
|
+
|
|
150
|
+
const fakeEc = {
|
|
151
|
+
waiting() {},
|
|
152
|
+
info() {},
|
|
153
|
+
warn() {},
|
|
154
|
+
error() {},
|
|
155
|
+
outCopy: async () => {}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
Module._load = function(request, parent, isMain) {
|
|
160
|
+
if (parent && parent.filename === runFile) {
|
|
161
|
+
if (request === '../epic') return fakeEc;
|
|
162
|
+
if (request === '../utils/momo-audio') return { playAudio() {} };
|
|
163
|
+
if (request === '../utils/momo-file-utils') return { exists: (p) => fsSync.existsSync(p) };
|
|
164
|
+
if (request === '../utils/momo-menu') {
|
|
165
|
+
return {
|
|
166
|
+
selectSingle: async (items, title) => {
|
|
167
|
+
menuTitles.push(title);
|
|
168
|
+
return items[0];
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (request === 'colors') return {};
|
|
173
|
+
}
|
|
174
|
+
return originalLoad.call(this, request, parent, isMain);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
process.cwd = () => root;
|
|
178
|
+
process.exit = (code) => {
|
|
179
|
+
throw new Error(`EXIT:${code}`);
|
|
180
|
+
};
|
|
181
|
+
console.log = () => {};
|
|
182
|
+
console.info = () => {};
|
|
183
|
+
console.warn = () => {};
|
|
184
|
+
console.error = () => {};
|
|
185
|
+
|
|
186
|
+
delete require.cache[runFile];
|
|
187
|
+
const executeRun = require(runFile);
|
|
188
|
+
await executeRun().catch((error) => {
|
|
189
|
+
if (!/^EXIT:\d+$/.test(error.message)) {
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
} finally {
|
|
194
|
+
delete require.cache[runFile];
|
|
195
|
+
Module._load = originalLoad;
|
|
196
|
+
process.cwd = originalCwd;
|
|
197
|
+
process.exit = originalExit;
|
|
198
|
+
console.log = originalLog;
|
|
199
|
+
console.info = originalInfo;
|
|
200
|
+
console.warn = originalWarn;
|
|
201
|
+
console.error = originalError;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
assert.deepStrictEqual(menuTitles, ['选择要执行的任务']);
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const testTaskUsesCurrentR2moDirectory = async () => {
|
|
209
|
+
await _withTempR2moDir(async (_root, r2moDir) => {
|
|
210
|
+
const result = _runTask(r2moDir, { ...process.env, PATH: '' });
|
|
211
|
+
|
|
212
|
+
const threadValue = (await fs.readFile(path.join(r2moDir, 'task', 'thread'), 'utf8')).trim();
|
|
213
|
+
assert.strictEqual(threadValue, '20');
|
|
214
|
+
assert.strictEqual(await _exists(r2moDir, path.join('task', _slotFilename(1))), true);
|
|
215
|
+
assert.strictEqual(await _exists(r2moDir, path.join('.r2mo', 'task', _slotFilename(1))), false);
|
|
216
|
+
assert.notStrictEqual(result.status, 1, result.stderr || result.stdout);
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const testRunUsesCurrentR2moDirectory = async () => {
|
|
221
|
+
const runFile = path.resolve(__dirname, 'executor', 'executeRun.js');
|
|
222
|
+
|
|
223
|
+
await _withTempR2moDir(async (_root, r2moDir) => {
|
|
224
|
+
const taskRoot = path.join(r2moDir, 'task');
|
|
225
|
+
await fs.mkdir(taskRoot, { recursive: true });
|
|
226
|
+
await fs.writeFile(path.join(taskRoot, _slotFilename(1)), _taskContent('R2MO 目录任务', '# Body'), 'utf8');
|
|
227
|
+
|
|
228
|
+
const menuTitles = [];
|
|
229
|
+
const copiedPrompts = [];
|
|
230
|
+
const originalLoad = Module._load;
|
|
231
|
+
const originalCwd = process.cwd;
|
|
232
|
+
const originalExit = process.exit;
|
|
233
|
+
const originalLog = console.log;
|
|
234
|
+
const originalInfo = console.info;
|
|
235
|
+
const originalWarn = console.warn;
|
|
236
|
+
const originalError = console.error;
|
|
237
|
+
|
|
238
|
+
const fakeEc = {
|
|
239
|
+
waiting() {},
|
|
240
|
+
info() {},
|
|
241
|
+
warn() {},
|
|
242
|
+
error() {},
|
|
243
|
+
outCopy: async (text) => {
|
|
244
|
+
copiedPrompts.push(text);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
Module._load = function(request, parent, isMain) {
|
|
250
|
+
if (parent && parent.filename === runFile) {
|
|
251
|
+
if (request === '../epic') return fakeEc;
|
|
252
|
+
if (request === '../utils/momo-audio') return { playAudio() {} };
|
|
253
|
+
if (request === '../utils/momo-menu') {
|
|
254
|
+
return {
|
|
255
|
+
selectSingle: async (items, title) => {
|
|
256
|
+
menuTitles.push(title);
|
|
257
|
+
return items[0];
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
if (request === 'colors') return {};
|
|
262
|
+
}
|
|
263
|
+
return originalLoad.call(this, request, parent, isMain);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
process.cwd = () => r2moDir;
|
|
267
|
+
process.exit = (code) => {
|
|
268
|
+
throw new Error(`EXIT:${code}`);
|
|
269
|
+
};
|
|
270
|
+
console.log = () => {};
|
|
271
|
+
console.info = () => {};
|
|
272
|
+
console.warn = () => {};
|
|
273
|
+
console.error = () => {};
|
|
274
|
+
|
|
275
|
+
delete require.cache[runFile];
|
|
276
|
+
const executeRun = require(runFile);
|
|
277
|
+
await executeRun().catch((error) => {
|
|
278
|
+
if (!/^EXIT:\d+$/.test(error.message)) {
|
|
279
|
+
throw error;
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
} finally {
|
|
283
|
+
delete require.cache[runFile];
|
|
284
|
+
Module._load = originalLoad;
|
|
285
|
+
process.cwd = originalCwd;
|
|
286
|
+
process.exit = originalExit;
|
|
287
|
+
console.log = originalLog;
|
|
288
|
+
console.info = originalInfo;
|
|
289
|
+
console.warn = originalWarn;
|
|
290
|
+
console.error = originalError;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
assert.deepStrictEqual(menuTitles, ['选择要执行的任务']);
|
|
294
|
+
assert.strictEqual(copiedPrompts.length, 1);
|
|
295
|
+
assert.match(copiedPrompts[0], /当前工作目录下 task\/task-001\.md /);
|
|
296
|
+
});
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const testRunPlaysAudioAfterSelection = async () => {
|
|
300
|
+
const runFile = path.resolve(__dirname, 'executor', 'executeRun.js');
|
|
301
|
+
|
|
302
|
+
await _withTempDir(async (root) => {
|
|
303
|
+
const taskRoot = path.join(root, TASK_DIR);
|
|
304
|
+
await fs.mkdir(taskRoot, { recursive: true });
|
|
305
|
+
await fs.writeFile(path.join(taskRoot, _slotFilename(1)), _taskContent('音效任务', '# Body'), 'utf8');
|
|
306
|
+
|
|
307
|
+
const audioCalls = [];
|
|
308
|
+
const selectionAudioState = [];
|
|
309
|
+
const originalLoad = Module._load;
|
|
310
|
+
const originalCwd = process.cwd;
|
|
311
|
+
const originalExit = process.exit;
|
|
312
|
+
const originalLog = console.log;
|
|
313
|
+
const originalInfo = console.info;
|
|
314
|
+
const originalWarn = console.warn;
|
|
315
|
+
const originalError = console.error;
|
|
316
|
+
|
|
317
|
+
const fakeEc = {
|
|
318
|
+
waiting() {},
|
|
319
|
+
info() {},
|
|
320
|
+
warn() {},
|
|
321
|
+
error() {},
|
|
322
|
+
outCopy: async () => {}
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
Module._load = function(request, parent, isMain) {
|
|
327
|
+
if (parent && parent.filename === runFile) {
|
|
328
|
+
if (request === '../epic') return fakeEc;
|
|
329
|
+
if (request === '../utils/momo-audio') {
|
|
330
|
+
return {
|
|
331
|
+
playAudio: (name) => {
|
|
332
|
+
audioCalls.push(name);
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
if (request === '../utils/momo-menu') {
|
|
337
|
+
return {
|
|
338
|
+
selectSingle: async (items) => {
|
|
339
|
+
selectionAudioState.push(audioCalls.slice());
|
|
340
|
+
return items[0];
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
if (request === 'colors') return {};
|
|
345
|
+
}
|
|
346
|
+
return originalLoad.call(this, request, parent, isMain);
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
process.cwd = () => root;
|
|
350
|
+
process.exit = (code) => {
|
|
351
|
+
throw new Error(`EXIT:${code}`);
|
|
352
|
+
};
|
|
353
|
+
console.log = () => {};
|
|
354
|
+
console.info = () => {};
|
|
355
|
+
console.warn = () => {};
|
|
356
|
+
console.error = () => {};
|
|
357
|
+
|
|
358
|
+
delete require.cache[runFile];
|
|
359
|
+
const executeRun = require(runFile);
|
|
360
|
+
await executeRun().catch((error) => {
|
|
361
|
+
if (!/^EXIT:\d+$/.test(error.message)) {
|
|
362
|
+
throw error;
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
} finally {
|
|
366
|
+
delete require.cache[runFile];
|
|
367
|
+
Module._load = originalLoad;
|
|
368
|
+
process.cwd = originalCwd;
|
|
369
|
+
process.exit = originalExit;
|
|
370
|
+
console.log = originalLog;
|
|
371
|
+
console.info = originalInfo;
|
|
372
|
+
console.warn = originalWarn;
|
|
373
|
+
console.error = originalError;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
assert.deepStrictEqual(selectionAudioState, [[]]);
|
|
377
|
+
assert.deepStrictEqual(audioCalls, ['audio/run.ogg']);
|
|
378
|
+
});
|
|
379
|
+
};
|
|
380
|
+
|
|
118
381
|
const main = async () => {
|
|
119
382
|
await testDefaultThreadFallsBackTo20();
|
|
120
383
|
await testThreadOverridesDefault();
|
|
121
384
|
await testShrinkThreadPrunesOverflow();
|
|
385
|
+
await testRunSkipsFocusModeSelection();
|
|
386
|
+
await testTaskUsesCurrentR2moDirectory();
|
|
387
|
+
await testRunUsesCurrentR2moDirectory();
|
|
388
|
+
await testRunPlaysAudioAfterSelection();
|
|
122
389
|
console.log('task tests passed');
|
|
123
390
|
};
|
|
124
391
|
|
package/.omc/project-memory.json
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": "1.0.0",
|
|
3
|
-
"lastScanned": 1776301703639,
|
|
4
|
-
"projectRoot": "/Users/lang/zero-cloud/app-zero/r2mo-matrix/r2mo-lain",
|
|
5
|
-
"techStack": {
|
|
6
|
-
"languages": [
|
|
7
|
-
{
|
|
8
|
-
"name": "JavaScript/TypeScript",
|
|
9
|
-
"version": null,
|
|
10
|
-
"confidence": "high",
|
|
11
|
-
"markers": [
|
|
12
|
-
"package.json"
|
|
13
|
-
]
|
|
14
|
-
}
|
|
15
|
-
],
|
|
16
|
-
"frameworks": [],
|
|
17
|
-
"packageManager": "npm",
|
|
18
|
-
"runtime": null
|
|
19
|
-
},
|
|
20
|
-
"build": {
|
|
21
|
-
"buildCommand": null,
|
|
22
|
-
"testCommand": "npm test",
|
|
23
|
-
"lintCommand": null,
|
|
24
|
-
"devCommand": null,
|
|
25
|
-
"scripts": {
|
|
26
|
-
"test": "src/index.test.js"
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
"conventions": {
|
|
30
|
-
"namingStyle": null,
|
|
31
|
-
"importStyle": null,
|
|
32
|
-
"testPattern": null,
|
|
33
|
-
"fileOrganization": null
|
|
34
|
-
},
|
|
35
|
-
"structure": {
|
|
36
|
-
"isMonorepo": false,
|
|
37
|
-
"workspaces": [],
|
|
38
|
-
"mainDirectories": [
|
|
39
|
-
"docs",
|
|
40
|
-
"src"
|
|
41
|
-
],
|
|
42
|
-
"gitBranches": {
|
|
43
|
-
"defaultBranch": "master",
|
|
44
|
-
"branchingStrategy": null
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
"customNotes": [],
|
|
48
|
-
"directoryMap": {
|
|
49
|
-
"docs": {
|
|
50
|
-
"path": "docs",
|
|
51
|
-
"purpose": "Documentation",
|
|
52
|
-
"fileCount": 0,
|
|
53
|
-
"lastAccessed": 1776301703596,
|
|
54
|
-
"keyFiles": []
|
|
55
|
-
},
|
|
56
|
-
"skills": {
|
|
57
|
-
"path": "skills",
|
|
58
|
-
"purpose": null,
|
|
59
|
-
"fileCount": 0,
|
|
60
|
-
"lastAccessed": 1776301703597,
|
|
61
|
-
"keyFiles": []
|
|
62
|
-
},
|
|
63
|
-
"src": {
|
|
64
|
-
"path": "src",
|
|
65
|
-
"purpose": "Source code",
|
|
66
|
-
"fileCount": 2,
|
|
67
|
-
"lastAccessed": 1776301703611,
|
|
68
|
-
"keyFiles": [
|
|
69
|
-
"lain.js",
|
|
70
|
-
"momo.js"
|
|
71
|
-
]
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
"hotPaths": [
|
|
75
|
-
{
|
|
76
|
-
"path": "src/executor/executeTask.js",
|
|
77
|
-
"accessCount": 8,
|
|
78
|
-
"lastAccessed": 1776352988772,
|
|
79
|
-
"type": "file"
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
"path": "src/executor/executeRun.js",
|
|
83
|
-
"accessCount": 6,
|
|
84
|
-
"lastAccessed": 1776302572942,
|
|
85
|
-
"type": "file"
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
"path": "src",
|
|
89
|
-
"accessCount": 6,
|
|
90
|
-
"lastAccessed": 1776302580506,
|
|
91
|
-
"type": "directory"
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
"path": "src/utils/momo-audio.js",
|
|
95
|
-
"accessCount": 5,
|
|
96
|
-
"lastAccessed": 1776302802009,
|
|
97
|
-
"type": "file"
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
"path": "src/_template/.obsidian/community-plugins.json",
|
|
101
|
-
"accessCount": 4,
|
|
102
|
-
"lastAccessed": 1775786409382,
|
|
103
|
-
"type": "file"
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
"path": ".obsidian/community-plugins.json",
|
|
107
|
-
"accessCount": 4,
|
|
108
|
-
"lastAccessed": 1775786409838,
|
|
109
|
-
"type": "file"
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
"path": "src/commander/task.json",
|
|
113
|
-
"accessCount": 2,
|
|
114
|
-
"lastAccessed": 1776352973218,
|
|
115
|
-
"type": "file"
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
"path": "src/_template/LAIN/.obsidian/community-plugins.json",
|
|
119
|
-
"accessCount": 1,
|
|
120
|
-
"lastAccessed": 1775786211402,
|
|
121
|
-
"type": "file"
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
"path": "package.json",
|
|
125
|
-
"accessCount": 1,
|
|
126
|
-
"lastAccessed": 1776302161925,
|
|
127
|
-
"type": "file"
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
"path": "src/commander",
|
|
131
|
-
"accessCount": 1,
|
|
132
|
-
"lastAccessed": 1776302162007,
|
|
133
|
-
"type": "directory"
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
"path": "src/commander/run.json",
|
|
137
|
-
"accessCount": 1,
|
|
138
|
-
"lastAccessed": 1776302188582,
|
|
139
|
-
"type": "file"
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
"path": "src/epic/momo.fn.out.js",
|
|
143
|
-
"accessCount": 1,
|
|
144
|
-
"lastAccessed": 1776302188717,
|
|
145
|
-
"type": "file"
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
"path": "src/momo.js",
|
|
149
|
-
"accessCount": 1,
|
|
150
|
-
"lastAccessed": 1776302402340,
|
|
151
|
-
"type": "file"
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
"path": "src/executor/index.js",
|
|
155
|
-
"accessCount": 1,
|
|
156
|
-
"lastAccessed": 1776302402605,
|
|
157
|
-
"type": "file"
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
"path": "src/utils/momo-menu.js",
|
|
161
|
-
"accessCount": 1,
|
|
162
|
-
"lastAccessed": 1776302439510,
|
|
163
|
-
"type": "file"
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
"path": "src/utils/momo-file-utils.js",
|
|
167
|
-
"accessCount": 1,
|
|
168
|
-
"lastAccessed": 1776302439713,
|
|
169
|
-
"type": "file"
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
"path": "src/executor/executeOpen.js",
|
|
173
|
-
"accessCount": 1,
|
|
174
|
-
"lastAccessed": 1776302439732,
|
|
175
|
-
"type": "file"
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
"path": "",
|
|
179
|
-
"accessCount": 1,
|
|
180
|
-
"lastAccessed": 1776302580716,
|
|
181
|
-
"type": "directory"
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
"path": "publish.sh",
|
|
185
|
-
"accessCount": 1,
|
|
186
|
-
"lastAccessed": 1776303501941,
|
|
187
|
-
"type": "file"
|
|
188
|
-
}
|
|
189
|
-
],
|
|
190
|
-
"userDirectives": []
|
|
191
|
-
}
|