momo-ai 1.0.20 → 1.0.21
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/.claude/skills/algorithmic-art/LICENSE.txt +202 -0
- package/.claude/skills/algorithmic-art/SKILL.md +405 -0
- package/.claude/skills/algorithmic-art/templates/generator_template.js +223 -0
- package/.claude/skills/algorithmic-art/templates/viewer.html +599 -0
- package/.claude/skills/r2mo-rad-lain/PROMPT.md +281 -0
- package/.claude/skills/r2mo-rad-lain/README.md +192 -0
- package/.claude/skills/r2mo-rad-lain/SKILL.md +412 -0
- package/.claude/skills/r2mo-rad-lain/examples/argument-parsing.js +154 -0
- package/.claude/skills/r2mo-rad-lain/examples/file-operations.js +182 -0
- package/.claude/skills/r2mo-rad-lain/file-utils-api.md +281 -0
- package/.claude/skills/r2mo-rad-lain/menu-api.md +187 -0
- package/.claude/skills/r2mo-rad-lain/scripts/file-utils.js +223 -0
- package/.claude/skills/r2mo-rad-lain/scripts/menu.js +289 -0
- package/.claude/skills/r2mo-rad-lain/scripts/yaml-parser.js +209 -0
- package/.claude/skills/r2mo-rad-lain/templates/command.json.template +13 -0
- package/.claude/skills/r2mo-rad-lain/templates/executor.js.template +32 -0
- package/.claude/skills/r2mo-rad-lain/templates/interactive-menu.js.template +221 -0
- package/.cursor/mcp.json +17 -0
- package/.obsidian/app.json +1 -0
- package/.obsidian/appearance.json +4 -0
- package/.obsidian/community-plugins.json +4 -0
- package/.obsidian/core-plugins.json +33 -0
- package/.obsidian/plugins/ai-agent/main.js +98495 -0
- package/.obsidian/plugins/ai-agent/manifest.json +11 -0
- package/.obsidian/plugins/ai-agent/styles.css +806 -0
- package/.obsidian/plugins/dataview/main.js +20876 -0
- package/.obsidian/plugins/dataview/manifest.json +11 -0
- package/.obsidian/plugins/dataview/styles.css +141 -0
- package/.obsidian/plugins/obsidian-excalidraw-plugin/main.js +10 -0
- package/.obsidian/plugins/obsidian-excalidraw-plugin/manifest.json +12 -0
- package/.obsidian/plugins/obsidian-excalidraw-plugin/styles.css +1 -0
- package/.obsidian/plugins/templater-obsidian/main.js +45 -0
- package/.obsidian/plugins/templater-obsidian/manifest.json +11 -0
- package/.obsidian/plugins/templater-obsidian/styles.css +226 -0
- package/.obsidian/plugins/terminal/main.js +200 -0
- package/.obsidian/plugins/terminal/manifest.json +14 -0
- package/.obsidian/plugins/terminal/styles.css +32 -0
- package/.obsidian/themes/AnuPpuccin/manifest.json +7 -0
- package/.obsidian/themes/AnuPpuccin/theme.css +9080 -0
- package/.obsidian/themes/Things/manifest.json +7 -0
- package/.obsidian/themes/Things/theme.css +1628 -0
- package/.obsidian/workspace.json +196 -0
- package/README.md +10 -123
- package/docs/images/logo.jpeg +0 -0
- package/install.sh +1 -0
- package/package.json +6 -2
- package/skills/r2mo-rad-lain/SKILL.md +101 -0
- package/src/_mcp/skills-server.mjs +70 -0
- package/src/_skill/repositories.json +16 -0
- package/src/commander/help.json +5 -0
- package/src/commander/mcp.json +13 -0
- package/src/commander/open.json +8 -2
- package/src/commander/skills.json +20 -0
- package/src/executor/executeEnv.js +48 -38
- package/src/executor/executeHelp.js +77 -16
- package/src/executor/executeInit.js +203 -149
- package/src/executor/executeMcp.js +290 -0
- package/src/executor/executeOpen.js +144 -125
- package/src/executor/executeSkills.js +747 -0
- package/src/executor/index.js +5 -39
- package/src/momo.js +2 -1
- package/src/utils/momo-args.js +39 -0
- package/src/utils/momo-file-utils.js +75 -0
- package/src/utils/momo-menu.js +54 -0
- package/src/commander/actor.json +0 -12
- package/src/commander/actors.json +0 -6
- package/src/commander/add.json +0 -12
- package/src/commander/agent.json +0 -12
- package/src/commander/agentcfg.json +0 -5
- package/src/commander/archive.json +0 -12
- package/src/commander/commit.json +0 -12
- package/src/commander/console.json +0 -7
- package/src/commander/lain.json +0 -7
- package/src/commander/list.json +0 -7
- package/src/commander/plan.json +0 -12
- package/src/commander/project.json +0 -12
- package/src/commander/pull.json +0 -6
- package/src/commander/push.json +0 -6
- package/src/commander/repo.json +0 -18
- package/src/commander/run.json +0 -18
- package/src/commander/show.json +0 -12
- package/src/commander/tasks.json +0 -18
- package/src/commander/unlock.json +0 -6
- package/src/commander/validate.json +0 -12
- package/src/executor/executeActor.js +0 -133
- package/src/executor/executeActors.js +0 -58
- package/src/executor/executeAdd.js +0 -307
- package/src/executor/executeAgent.js +0 -299
- package/src/executor/executeAgentCfg.js +0 -210
- package/src/executor/executeArchive.js +0 -124
- package/src/executor/executeCommit.js +0 -202
- package/src/executor/executeConsole.js +0 -142
- package/src/executor/executeList.js +0 -133
- package/src/executor/executePlan.js +0 -164
- package/src/executor/executeProject.js +0 -313
- package/src/executor/executePull.js +0 -127
- package/src/executor/executePush.js +0 -243
- package/src/executor/executeRepo.js +0 -238
- package/src/executor/executeRun.js +0 -644
- package/src/executor/executeShow.js +0 -164
- package/src/executor/executeTasks.js +0 -384
- package/src/executor/executeUnlock.js +0 -110
- package/src/executor/executeValidate.js +0 -210
package/src/executor/index.js
CHANGED
|
@@ -1,50 +1,16 @@
|
|
|
1
1
|
const executeHelp = require('./executeHelp');
|
|
2
2
|
const executeInit = require('./executeInit');
|
|
3
|
-
const executeRepo = require('./executeRepo');
|
|
4
|
-
const executeAdd = require('./executeAdd');
|
|
5
|
-
const executePlan = require('./executePlan');
|
|
6
|
-
const executeArchive = require('./executeArchive');
|
|
7
3
|
const executeEnv = require('./executeEnv');
|
|
8
4
|
const executeOpen = require('./executeOpen');
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const executeValidate = require('./executeValidate');
|
|
12
|
-
const executeActor = require('./executeActor');
|
|
13
|
-
const executeActors = require('./executeActors');
|
|
14
|
-
const executeRun = require('./executeRun');
|
|
15
|
-
const executeTasks = require('./executeTasks');
|
|
16
|
-
const executeUnlock = require('./executeUnlock');
|
|
17
|
-
const executeProject = require('./executeProject');
|
|
18
|
-
const executeAgentCfg = require('./executeAgentCfg');
|
|
19
|
-
const executeAgent = require('./executeAgent');
|
|
20
|
-
const executeConsole = require('./executeConsole');
|
|
21
|
-
const executePull = require('./executePull');
|
|
22
|
-
const executeCommit = require('./executeCommit');
|
|
23
|
-
const executePush = require('./executePush');
|
|
5
|
+
const executeSkills = require('./executeSkills');
|
|
6
|
+
const executeMcp = require('./executeMcp');
|
|
24
7
|
|
|
25
8
|
const exported = {
|
|
26
9
|
executeHelp,
|
|
27
10
|
executeInit,
|
|
28
|
-
executeRepo,
|
|
29
|
-
executeAdd,
|
|
30
|
-
executePlan,
|
|
31
|
-
executeArchive,
|
|
32
11
|
executeEnv,
|
|
33
12
|
executeOpen,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
executeValidate,
|
|
37
|
-
executeActor,
|
|
38
|
-
executeActors,
|
|
39
|
-
executeRun,
|
|
40
|
-
executeTasks,
|
|
41
|
-
executeUnlock,
|
|
42
|
-
executeProject,
|
|
43
|
-
executeAgentCfg,
|
|
44
|
-
executeAgent,
|
|
45
|
-
executeConsole,
|
|
46
|
-
executePull,
|
|
47
|
-
executeCommit,
|
|
48
|
-
executePush
|
|
13
|
+
executeSkills,
|
|
14
|
+
executeMcp
|
|
49
15
|
};
|
|
50
|
-
module.exports = exported;
|
|
16
|
+
module.exports = exported;
|
package/src/momo.js
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module args
|
|
3
|
+
* 高级参数解析工具
|
|
4
|
+
*/
|
|
5
|
+
const Ec = require('../epic');
|
|
6
|
+
|
|
7
|
+
// 标准解析 (key-value)
|
|
8
|
+
const parseStandard = (options) => Ec.parseArgument(options);
|
|
9
|
+
|
|
10
|
+
// 解析可选值 (-r [val]) -> { hasFlag, value }
|
|
11
|
+
const parseOptional = (flag, alias) => {
|
|
12
|
+
const args = process.argv.slice(3);
|
|
13
|
+
for (let i = 0; i < args.length; i++) {
|
|
14
|
+
if (args[i] === `-${alias}` || args[i] === `--${flag}`) {
|
|
15
|
+
const next = args[i+1];
|
|
16
|
+
return { hasFlag: true, value: (next && !next.startsWith('-')) ? next : null };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return { hasFlag: false, value: null };
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// 解析布尔值 (-v) -> boolean
|
|
23
|
+
const parseBool = (flag, alias) => {
|
|
24
|
+
const args = process.argv.slice(3);
|
|
25
|
+
return args.includes(`--${flag}`) || args.includes(`-${alias}`);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// 解析位置参数 -> array
|
|
29
|
+
const parsePositional = () => {
|
|
30
|
+
const args = process.argv.slice(3);
|
|
31
|
+
const res = [];
|
|
32
|
+
for (let i = 0; i < args.length; i++) {
|
|
33
|
+
if (args[i].startsWith('-')) { i++; continue; } // Skip flags and their potential values (simple heuristic)
|
|
34
|
+
res.push(args[i]);
|
|
35
|
+
}
|
|
36
|
+
return res;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
module.exports = { parseStandard, parseOptional, parseBool, parsePositional };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module momo-fs
|
|
3
|
+
* 文件、目录、YAML 解析与 Git 操作的集合库
|
|
4
|
+
*/
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
const fsAsync = require('fs').promises;
|
|
9
|
+
const { execSync } = require('child_process');
|
|
10
|
+
|
|
11
|
+
// --- 基础文件操作 ---
|
|
12
|
+
const ensureDir = async (p) => fsAsync.mkdir(p, { recursive: true });
|
|
13
|
+
const exists = (p) => fs.existsSync(p);
|
|
14
|
+
const readJson = (p) => exists(p) ? JSON.parse(fs.readFileSync(p, 'utf8')) : null;
|
|
15
|
+
const writeJson = async (p, d, i = 4) => {
|
|
16
|
+
await ensureDir(path.dirname(p));
|
|
17
|
+
await fsAsync.writeFile(p, JSON.stringify(d, null, i));
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const copyDir = async (src, dest) => {
|
|
21
|
+
await ensureDir(dest);
|
|
22
|
+
const items = await fsAsync.readdir(src);
|
|
23
|
+
for (const item of items) {
|
|
24
|
+
const sPath = path.join(src, item), dPath = path.join(dest, item);
|
|
25
|
+
(await fsAsync.stat(sPath)).isDirectory() ? await copyDir(sPath, dPath) : await fsAsync.copyFile(sPath, dPath);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const scanDir = (dir, filter = () => true) => {
|
|
30
|
+
if (!exists(dir)) return [];
|
|
31
|
+
return fs.readdirSync(dir).reduce((acc, item) => {
|
|
32
|
+
const p = path.join(dir, item);
|
|
33
|
+
try {
|
|
34
|
+
if (fs.statSync(p).isDirectory() && filter(item, p)) acc.push({ name: item, path: p, isDirectory: true });
|
|
35
|
+
} catch (e) {}
|
|
36
|
+
return acc;
|
|
37
|
+
}, []);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// --- 临时目录 ---
|
|
41
|
+
const createTempDir = (pre = 'momo') => path.join(os.tmpdir(), `.${pre}-${Date.now()}`);
|
|
42
|
+
const cleanup = async (p) => p && exists(p) ? fsAsync.rm(p, { recursive: true, force: true }).then(() => true).catch(() => false) : false;
|
|
43
|
+
|
|
44
|
+
// --- Git ---
|
|
45
|
+
const gitClone = (url, dest, { shallow = true } = {}) => {
|
|
46
|
+
try {
|
|
47
|
+
execSync(`git clone ${shallow ? '--depth 1' : ''} "${url}" "${dest}"`, { stdio: 'ignore' });
|
|
48
|
+
return true;
|
|
49
|
+
} catch (e) { throw new Error(`Git clone failed: ${e.message}`); }
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// --- YAML ---
|
|
53
|
+
const parseYaml = (str) => { // 简易解析
|
|
54
|
+
const meta = {}; let arrKey = null;
|
|
55
|
+
str.split('\n').forEach(line => {
|
|
56
|
+
if (!line.trim() || line.trim().startsWith('#')) return;
|
|
57
|
+
const arrM = line.match(/^(\s+)-\s+(.+)$/);
|
|
58
|
+
if (arrM && arrKey) { (meta[arrKey] = meta[arrKey] || []).push(arrM[2].trim().replace(/^["']|["']$/g, '')); return; }
|
|
59
|
+
const kvM = line.match(/^(\w[\w-]*):\s*(.*)$/);
|
|
60
|
+
if (kvM) {
|
|
61
|
+
const k = kvM[1], v = kvM[2].trim().replace(/^["']|["']$/g, '');
|
|
62
|
+
if (!v) { arrKey = k; meta[k] = []; } else { meta[k] = v === 'true' ? true : v === 'false' ? false : !isNaN(v) ? Number(v) : v; arrKey = null; }
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return meta;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const parseFile = (p) => {
|
|
69
|
+
if (!exists(p)) return null;
|
|
70
|
+
const content = fs.readFileSync(p, 'utf8');
|
|
71
|
+
const match = content.match(/^---\n([\s\S]+?)\n---/);
|
|
72
|
+
return match ? { attributes: parseYaml(match[1]), body: content.replace(match[0], '').trim() } : null;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
module.exports = { copyDir, scanDir, createTempDir, cleanup, ensureDir, exists, readJson, writeJson, gitClone, parseFile, parseYaml };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module momo-menu
|
|
3
|
+
* 交互式菜单 (Raw Mode)
|
|
4
|
+
*/
|
|
5
|
+
const readline = require('readline');
|
|
6
|
+
const clearScreen = () => process.stdout.write('\x1B[2J\x1B[0f');
|
|
7
|
+
|
|
8
|
+
const _baseSelect = (items, title, isMulti) => new Promise(resolve => {
|
|
9
|
+
let cursor = 0;
|
|
10
|
+
const selected = new Array(items.length).fill(false);
|
|
11
|
+
const maxLen = Math.max(...items.map(i => (i.name||'').length), 4);
|
|
12
|
+
|
|
13
|
+
const render = () => {
|
|
14
|
+
clearScreen();
|
|
15
|
+
console.log(`\n[Momo AI]`.blue.bold + ` ====== ${title} ======`.blue + '\n');
|
|
16
|
+
items.forEach((item, i) => {
|
|
17
|
+
const active = i === cursor, check = isMulti ? (selected[i] ? '[✓]'.green : '[ ]') : '';
|
|
18
|
+
console.log(` ${active ? '▸'.cyan : ' '} ${check} ${(item.name||'').padEnd(maxLen)[active?'cyan':'reset']}${active?''.bold:''} ${(item.description||'').gray}`);
|
|
19
|
+
});
|
|
20
|
+
console.log('\n ' + (isMulti ? 'Space:Toggle A:All N:None ' : '') + 'Enter:Confirm Q:Quit'.gray + '\n');
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
readline.emitKeypressEvents(process.stdin);
|
|
24
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
25
|
+
render();
|
|
26
|
+
|
|
27
|
+
const onKey = (str, key) => {
|
|
28
|
+
if (!key) return;
|
|
29
|
+
if ((key.ctrl && key.name === 'c') || key.name === 'q' || key.name === 'escape') {
|
|
30
|
+
cleanup(); resolve(isMulti ? { indices: [], items: [] } : null); return;
|
|
31
|
+
}
|
|
32
|
+
if (key.name === 'up') { cursor = cursor > 0 ? cursor - 1 : items.length - 1; render(); }
|
|
33
|
+
if (key.name === 'down') { cursor = cursor < items.length - 1 ? cursor + 1 : 0; render(); }
|
|
34
|
+
if (isMulti) {
|
|
35
|
+
if (key.name === 'space') { selected[cursor] = !selected[cursor]; render(); }
|
|
36
|
+
if (key.name === 'a') { selected.fill(true); render(); }
|
|
37
|
+
if (key.name === 'n') { selected.fill(false); render(); }
|
|
38
|
+
}
|
|
39
|
+
if (key.name === 'return') {
|
|
40
|
+
cleanup();
|
|
41
|
+
if (!isMulti) resolve(items[cursor]);
|
|
42
|
+
else resolve({ indices: selected.map((v, i) => v ? i : -1).filter(i => i !== -1), items: items.filter((_, i) => selected[i]) });
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const cleanup = () => { process.stdin.setRawMode(false); process.stdin.removeListener('keypress', onKey); clearScreen(); };
|
|
47
|
+
process.stdin.on('keypress', onKey);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
module.exports = {
|
|
51
|
+
selectMultiple: (items, title) => _baseSelect(items, title || 'Select Options', true),
|
|
52
|
+
selectSingle: (items, title) => _baseSelect(items, title || 'Select One', false),
|
|
53
|
+
clearScreen
|
|
54
|
+
};
|
package/src/commander/actor.json
DELETED
package/src/commander/add.json
DELETED
package/src/commander/agent.json
DELETED
package/src/commander/lain.json
DELETED
package/src/commander/list.json
DELETED
package/src/commander/plan.json
DELETED
package/src/commander/pull.json
DELETED
package/src/commander/push.json
DELETED
package/src/commander/repo.json
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"executor": "executeRepo",
|
|
3
|
-
"description": "Clone 仓库中的代码到工作区!",
|
|
4
|
-
"command": "repo",
|
|
5
|
-
"options": [
|
|
6
|
-
{
|
|
7
|
-
"name": "address",
|
|
8
|
-
"alias": "a",
|
|
9
|
-
"description": "当前项目的远程 Git 仓库地址!"
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
"name": "instance",
|
|
13
|
-
"alias": "i",
|
|
14
|
-
"description": "需要 Clone 的远程仓库的实例副本数量!",
|
|
15
|
-
"default": 10
|
|
16
|
-
}
|
|
17
|
-
]
|
|
18
|
-
}
|
package/src/commander/run.json
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"executor": "executeRun",
|
|
3
|
-
"description": "(CV)执行指定的任务!",
|
|
4
|
-
"command": "run",
|
|
5
|
-
"options": [
|
|
6
|
-
{
|
|
7
|
-
"name": "actor",
|
|
8
|
-
"alias": "a",
|
|
9
|
-
"description": "指定执行任务的 Actor 名称",
|
|
10
|
-
"default": "unknown"
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
"name": "task",
|
|
14
|
-
"alias": "t",
|
|
15
|
-
"description": "任务名称"
|
|
16
|
-
}
|
|
17
|
-
]
|
|
18
|
-
}
|
package/src/commander/show.json
DELETED
package/src/commander/tasks.json
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"executor": "executeTasks",
|
|
3
|
-
"description": "(CV)枚举所有任务信息!",
|
|
4
|
-
"command": "tasks",
|
|
5
|
-
"options": [
|
|
6
|
-
{
|
|
7
|
-
"name": "actor",
|
|
8
|
-
"alias": "a",
|
|
9
|
-
"description": "指定执行任务的 Actor 名称",
|
|
10
|
-
"default": "unknown"
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
"name": "task",
|
|
14
|
-
"alias": "t",
|
|
15
|
-
"description": "任务名称"
|
|
16
|
-
}
|
|
17
|
-
]
|
|
18
|
-
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
const Ec = require('../epic');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 交互式选择LLM
|
|
7
|
-
*/
|
|
8
|
-
const _selectLLM = async (actorName) => {
|
|
9
|
-
Ec.waiting('🤖 请选择要使用的LLM:');
|
|
10
|
-
|
|
11
|
-
const llmOptions = [
|
|
12
|
-
{name: 'OpenSpec', value: 'openspec'},
|
|
13
|
-
{name: 'SpecKit', value: 'spec-kit'},
|
|
14
|
-
{name: 'Kiro', value: 'kiro'},
|
|
15
|
-
{name: 'Trea', value: 'trea'},
|
|
16
|
-
{name: 'Cursor', value: 'cursor'},
|
|
17
|
-
{name: 'Lingma', value: 'lingma'},
|
|
18
|
-
{name: 'Qoder', value: 'qoder'},
|
|
19
|
-
{name: 'WindSurf', value: 'windsurf'},
|
|
20
|
-
{name: 'GitHub Copilot', value: 'github'},
|
|
21
|
-
{name: 'Claude Code', value: 'claude-code'},
|
|
22
|
-
{name: 'ChatGPT', value: 'chatgpt'}
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
// 显示选项
|
|
26
|
-
llmOptions.forEach((option, index) => {
|
|
27
|
-
Ec.waiting(`${index + 1}. ${option.name}`);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
// 获取用户选择
|
|
31
|
-
const answer = await Ec.ask('请输入选项编号: ');
|
|
32
|
-
const selectedIndex = parseInt(answer) - 1;
|
|
33
|
-
|
|
34
|
-
// 验证选择
|
|
35
|
-
if (isNaN(selectedIndex) || selectedIndex < 0 || selectedIndex >= llmOptions.length) {
|
|
36
|
-
Ec.error('❌ 无效的选择');
|
|
37
|
-
return _selectLLM(actorName); // 重新选择
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const selectedLLM = llmOptions[selectedIndex];
|
|
41
|
-
|
|
42
|
-
// 获取 token 配置信息
|
|
43
|
-
const token = await Ec.ask(`请输入 ${selectedLLM.name} 的 Token (可选): `);
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
llm: selectedLLM.value,
|
|
47
|
-
name: actorName,
|
|
48
|
-
token: token || ''
|
|
49
|
-
};
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
module.exports = async (options) => {
|
|
53
|
-
// 参数提取
|
|
54
|
-
const parsed = Ec.parseArgument(options);
|
|
55
|
-
const actorName = parsed.actor || parsed.a;
|
|
56
|
-
|
|
57
|
-
if (!actorName) {
|
|
58
|
-
Ec.error("❌ 请提供角色名称 (-a, --actor)");
|
|
59
|
-
process.exit(1);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// 在 Windows 上检查非法字符
|
|
63
|
-
if (process.platform === 'win32') {
|
|
64
|
-
const illegalChars = /[<>:"/\\|?*\x00-\x1F]/g;
|
|
65
|
-
if (illegalChars.test(actorName)) {
|
|
66
|
-
Ec.error(`❌ 角色名称 "${actorName}" 包含非法字符,请避免使用以下字符: < > : " / \\ | ? * 以及控制字符`);
|
|
67
|
-
process.exit(1);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
// 检查 specification/actor 目录是否存在
|
|
73
|
-
const actorBaseDir = path.resolve(process.cwd(), 'specification', 'actor');
|
|
74
|
-
if (!fs.existsSync(actorBaseDir)) {
|
|
75
|
-
Ec.error("❌ 未找到 specification/actor 目录,请先初始化项目");
|
|
76
|
-
process.exit(1);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// 检查角色是否已存在
|
|
80
|
-
const actorDir = path.resolve(actorBaseDir, actorName);
|
|
81
|
-
if (fs.existsSync(actorDir)) {
|
|
82
|
-
Ec.error(`❌ 角色 "${actorName}" 已存在`);
|
|
83
|
-
process.exit(1);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// 创建角色目录
|
|
87
|
-
if (!fs.existsSync(actorDir)) {
|
|
88
|
-
fs.mkdirSync(actorDir, { recursive: true });
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// 从模板复制内容到stack.md和limit.md
|
|
92
|
-
const templatePath = path.resolve(__dirname, '../_template/LAIN/.momo/advanced/actor.md');
|
|
93
|
-
if (fs.existsSync(templatePath)) {
|
|
94
|
-
const templateContent = fs.readFileSync(templatePath, 'utf8');
|
|
95
|
-
|
|
96
|
-
// 提取技术栈和限制信息
|
|
97
|
-
const stackMatch = templateContent.match(/## 技术栈([\s\S]*?)## 限制/);
|
|
98
|
-
const limitMatch = templateContent.match(/## 限制([\s\S]*)/);
|
|
99
|
-
|
|
100
|
-
if (stackMatch && limitMatch) {
|
|
101
|
-
const stackContent = `# 技术栈信息\n\n${stackMatch[1].trim()}\n`;
|
|
102
|
-
const stackPath = path.resolve(actorDir, 'stack.md');
|
|
103
|
-
fs.writeFileSync(stackPath, stackContent);
|
|
104
|
-
Ec.waiting(`📄 创建技术栈文件: ${stackPath}`);
|
|
105
|
-
|
|
106
|
-
const limitContent = `# 角色限制\n\n${limitMatch[1].trim()}\n`;
|
|
107
|
-
const limitPath = path.resolve(actorDir, 'limit.md');
|
|
108
|
-
fs.writeFileSync(limitPath, limitContent);
|
|
109
|
-
Ec.waiting(`📄 创建限制文件: ${limitPath}`);
|
|
110
|
-
|
|
111
|
-
// 创建config.json文件
|
|
112
|
-
const configPath = path.resolve(actorDir, 'config.json');
|
|
113
|
-
fs.writeFileSync(configPath, JSON.stringify(llmConfig, null, 4));
|
|
114
|
-
Ec.waiting(`📄 创建配置文件: ${configPath}`);
|
|
115
|
-
|
|
116
|
-
Ec.info(`✅ 成功添加新角色 "${actorName}"`);
|
|
117
|
-
process.exit(0);
|
|
118
|
-
} else {
|
|
119
|
-
Ec.error("❌ 模板文件格式不正确");
|
|
120
|
-
process.exit(1);
|
|
121
|
-
}
|
|
122
|
-
} else {
|
|
123
|
-
Ec.error(`❌ 未找到模板文件: ${templatePath}`);
|
|
124
|
-
process.exit(1);
|
|
125
|
-
}
|
|
126
|
-
} catch (error) {
|
|
127
|
-
Ec.error(`❌ 执行过程中发生错误: ${error.message}`);
|
|
128
|
-
if (process.platform === 'win32') {
|
|
129
|
-
Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
|
|
130
|
-
}
|
|
131
|
-
process.exit(1);
|
|
132
|
-
}
|
|
133
|
-
};
|