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.
Files changed (103) hide show
  1. package/.claude/skills/algorithmic-art/LICENSE.txt +202 -0
  2. package/.claude/skills/algorithmic-art/SKILL.md +405 -0
  3. package/.claude/skills/algorithmic-art/templates/generator_template.js +223 -0
  4. package/.claude/skills/algorithmic-art/templates/viewer.html +599 -0
  5. package/.claude/skills/r2mo-rad-lain/PROMPT.md +281 -0
  6. package/.claude/skills/r2mo-rad-lain/README.md +192 -0
  7. package/.claude/skills/r2mo-rad-lain/SKILL.md +412 -0
  8. package/.claude/skills/r2mo-rad-lain/examples/argument-parsing.js +154 -0
  9. package/.claude/skills/r2mo-rad-lain/examples/file-operations.js +182 -0
  10. package/.claude/skills/r2mo-rad-lain/file-utils-api.md +281 -0
  11. package/.claude/skills/r2mo-rad-lain/menu-api.md +187 -0
  12. package/.claude/skills/r2mo-rad-lain/scripts/file-utils.js +223 -0
  13. package/.claude/skills/r2mo-rad-lain/scripts/menu.js +289 -0
  14. package/.claude/skills/r2mo-rad-lain/scripts/yaml-parser.js +209 -0
  15. package/.claude/skills/r2mo-rad-lain/templates/command.json.template +13 -0
  16. package/.claude/skills/r2mo-rad-lain/templates/executor.js.template +32 -0
  17. package/.claude/skills/r2mo-rad-lain/templates/interactive-menu.js.template +221 -0
  18. package/.cursor/mcp.json +17 -0
  19. package/.obsidian/app.json +1 -0
  20. package/.obsidian/appearance.json +4 -0
  21. package/.obsidian/community-plugins.json +4 -0
  22. package/.obsidian/core-plugins.json +33 -0
  23. package/.obsidian/plugins/ai-agent/main.js +98495 -0
  24. package/.obsidian/plugins/ai-agent/manifest.json +11 -0
  25. package/.obsidian/plugins/ai-agent/styles.css +806 -0
  26. package/.obsidian/plugins/dataview/main.js +20876 -0
  27. package/.obsidian/plugins/dataview/manifest.json +11 -0
  28. package/.obsidian/plugins/dataview/styles.css +141 -0
  29. package/.obsidian/plugins/obsidian-excalidraw-plugin/main.js +10 -0
  30. package/.obsidian/plugins/obsidian-excalidraw-plugin/manifest.json +12 -0
  31. package/.obsidian/plugins/obsidian-excalidraw-plugin/styles.css +1 -0
  32. package/.obsidian/plugins/templater-obsidian/main.js +45 -0
  33. package/.obsidian/plugins/templater-obsidian/manifest.json +11 -0
  34. package/.obsidian/plugins/templater-obsidian/styles.css +226 -0
  35. package/.obsidian/plugins/terminal/main.js +200 -0
  36. package/.obsidian/plugins/terminal/manifest.json +14 -0
  37. package/.obsidian/plugins/terminal/styles.css +32 -0
  38. package/.obsidian/themes/AnuPpuccin/manifest.json +7 -0
  39. package/.obsidian/themes/AnuPpuccin/theme.css +9080 -0
  40. package/.obsidian/themes/Things/manifest.json +7 -0
  41. package/.obsidian/themes/Things/theme.css +1628 -0
  42. package/.obsidian/workspace.json +196 -0
  43. package/README.md +10 -123
  44. package/docs/images/logo.jpeg +0 -0
  45. package/install.sh +1 -0
  46. package/package.json +6 -2
  47. package/skills/r2mo-rad-lain/SKILL.md +101 -0
  48. package/src/_mcp/skills-server.mjs +70 -0
  49. package/src/_skill/repositories.json +16 -0
  50. package/src/commander/help.json +5 -0
  51. package/src/commander/mcp.json +13 -0
  52. package/src/commander/open.json +8 -2
  53. package/src/commander/skills.json +20 -0
  54. package/src/executor/executeEnv.js +48 -38
  55. package/src/executor/executeHelp.js +77 -16
  56. package/src/executor/executeInit.js +203 -149
  57. package/src/executor/executeMcp.js +290 -0
  58. package/src/executor/executeOpen.js +144 -125
  59. package/src/executor/executeSkills.js +747 -0
  60. package/src/executor/index.js +5 -39
  61. package/src/momo.js +2 -1
  62. package/src/utils/momo-args.js +39 -0
  63. package/src/utils/momo-file-utils.js +75 -0
  64. package/src/utils/momo-menu.js +54 -0
  65. package/src/commander/actor.json +0 -12
  66. package/src/commander/actors.json +0 -6
  67. package/src/commander/add.json +0 -12
  68. package/src/commander/agent.json +0 -12
  69. package/src/commander/agentcfg.json +0 -5
  70. package/src/commander/archive.json +0 -12
  71. package/src/commander/commit.json +0 -12
  72. package/src/commander/console.json +0 -7
  73. package/src/commander/lain.json +0 -7
  74. package/src/commander/list.json +0 -7
  75. package/src/commander/plan.json +0 -12
  76. package/src/commander/project.json +0 -12
  77. package/src/commander/pull.json +0 -6
  78. package/src/commander/push.json +0 -6
  79. package/src/commander/repo.json +0 -18
  80. package/src/commander/run.json +0 -18
  81. package/src/commander/show.json +0 -12
  82. package/src/commander/tasks.json +0 -18
  83. package/src/commander/unlock.json +0 -6
  84. package/src/commander/validate.json +0 -12
  85. package/src/executor/executeActor.js +0 -133
  86. package/src/executor/executeActors.js +0 -58
  87. package/src/executor/executeAdd.js +0 -307
  88. package/src/executor/executeAgent.js +0 -299
  89. package/src/executor/executeAgentCfg.js +0 -210
  90. package/src/executor/executeArchive.js +0 -124
  91. package/src/executor/executeCommit.js +0 -202
  92. package/src/executor/executeConsole.js +0 -142
  93. package/src/executor/executeList.js +0 -133
  94. package/src/executor/executePlan.js +0 -164
  95. package/src/executor/executeProject.js +0 -313
  96. package/src/executor/executePull.js +0 -127
  97. package/src/executor/executePush.js +0 -243
  98. package/src/executor/executeRepo.js +0 -238
  99. package/src/executor/executeRun.js +0 -644
  100. package/src/executor/executeShow.js +0 -164
  101. package/src/executor/executeTasks.js +0 -384
  102. package/src/executor/executeUnlock.js +0 -110
  103. package/src/executor/executeValidate.js +0 -210
@@ -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 executeShow = require('./executeShow');
10
- const executeList = require('./executeList');
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
- executeShow,
35
- executeList,
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
@@ -28,7 +28,8 @@ if (_isVersionMode()) {
28
28
  }
29
29
 
30
30
  // 输出头部
31
- Ec.executeHeader("Rachel Momo / SDD");
31
+ Ec.info("SDD / Spec Driven Development ...")
32
+ // Ec.executeHeader("Rachel Momo / SDD");
32
33
 
33
34
  // 读取配置文件
34
35
  const configArr = Ec.parseMetadata();
@@ -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
+ };
@@ -1,12 +0,0 @@
1
- {
2
- "executor": "executeActor",
3
- "description": "创建或更新指定的AI角色信息!",
4
- "command": "actor",
5
- "options": [
6
- {
7
- "name": "name",
8
- "alias": "n",
9
- "description": "角色名称!"
10
- }
11
- ]
12
- }
@@ -1,6 +0,0 @@
1
- {
2
- "executor": "executeActors",
3
- "description": "列出所有已存在的AI角色信息!",
4
- "command": "actors",
5
- "options": []
6
- }
@@ -1,12 +0,0 @@
1
- {
2
- "executor": "executeAdd",
3
- "description": "(CV)添加新需求,并将 prompt 追加到剪切板中!",
4
- "command": "add",
5
- "options": [
6
- {
7
- "name": "name",
8
- "alias": "n",
9
- "description": "需求名称或者需求文件的 .md 路径"
10
- }
11
- ]
12
- }
@@ -1,12 +0,0 @@
1
- {
2
- "executor": "executeAgent",
3
- "description": "加载并复制指定 Agent 的提示词到剪切板",
4
- "command": "agent",
5
- "options": [
6
- {
7
- "name": "agent",
8
- "alias": "a",
9
- "description": "智能体名称(Arg Name)"
10
- }
11
- ]
12
- }
@@ -1,5 +0,0 @@
1
- {
2
- "executor": "executeAgentCfg",
3
- "description": "配置和打开系统内置 agents",
4
- "command": "agentcfg"
5
- }
@@ -1,12 +0,0 @@
1
- {
2
- "executor": "executeArchive",
3
- "description": "将某个需求归档操作,会删除原始需求信息!",
4
- "command": "archive",
5
- "options": [
6
- {
7
- "name": "name",
8
- "alias": "n",
9
- "description": "需求名称,不可以是 .md 的文档,而且需求必须存在!"
10
- }
11
- ]
12
- }
@@ -1,12 +0,0 @@
1
- {
2
- "executor": "executeCommit",
3
- "description": "提交所有副本的更改到本地仓库!",
4
- "command": "commit",
5
- "options": [
6
- {
7
- "name": "message",
8
- "alias": "m",
9
- "description": "提交信息"
10
- }
11
- ]
12
- }
@@ -1,7 +0,0 @@
1
- {
2
- "executor": "executeConsole",
3
- "description": "启动交互式控制台",
4
- "command": "console",
5
- "options": [
6
- ]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "executor": "executeConsole",
3
- "description": "启动交互式控制台",
4
- "command": "lain",
5
- "options": [
6
- ]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "executor": "executeList",
3
- "description": "列举所有需求列表!",
4
- "command": "list",
5
- "options": [
6
- ]
7
- }
@@ -1,12 +0,0 @@
1
- {
2
- "executor": "executePlan",
3
- "description": "(CV)生成新的需求计划,重写需求信息!",
4
- "command": "plan",
5
- "options": [
6
- {
7
- "name": "name",
8
- "alias": "n",
9
- "description": "需求名称,不可以是 .md 的文档,而且需求必须存在!"
10
- }
11
- ]
12
- }
@@ -1,12 +0,0 @@
1
- {
2
- "executor": "executeProject",
3
- "description": "引用外部项目到 reference 目录中",
4
- "command": "project",
5
- "options": [
6
- {
7
- "name": "source",
8
- "alias": "s",
9
- "description": "项目路径,必须提供"
10
- }
11
- ]
12
- }
@@ -1,6 +0,0 @@
1
- {
2
- "executor": "executePull",
3
- "description": "从远程仓库拉取所有副本的最新代码!",
4
- "command": "pull",
5
- "options": []
6
- }
@@ -1,6 +0,0 @@
1
- {
2
- "executor": "executePush",
3
- "description": "推送所有副本的更改到远程仓库!",
4
- "command": "push",
5
- "options": []
6
- }
@@ -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
- }
@@ -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
- }
@@ -1,12 +0,0 @@
1
- {
2
- "executor": "executeShow",
3
- "description": "显示指定需求的详细内容!",
4
- "command": "show",
5
- "options": [
6
- {
7
- "name": "name",
8
- "alias": "n",
9
- "description": "需求名称!"
10
- }
11
- ]
12
- }
@@ -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,6 +0,0 @@
1
- {
2
- "executor": "executeUnlock",
3
- "description": "解锁所有被占用的任务和工作空间!",
4
- "command": "unlock",
5
- "options": []
6
- }
@@ -1,12 +0,0 @@
1
- {
2
- "executor": "executeValidate",
3
- "description": "验证指定需求格式是否合法!",
4
- "command": "validate",
5
- "options": [
6
- {
7
- "name": "name",
8
- "alias": "n",
9
- "description": "需求名称!"
10
- }
11
- ]
12
- }
@@ -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
- };