momo-ai 1.0.6 → 1.0.8
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 +33 -1
- package/package.json +3 -2
- package/src/_template/LAIN/AGENTS.md +93 -86
- package/src/commander/console.json +7 -0
- package/src/commander/lain.json +7 -0
- package/src/executor/executeConsole.js +115 -0
- package/src/executor/executeInit.js +92 -1
- package/src/executor/index.js +3 -1
- package/src/lain.js +141 -0
- package/src/terminal/commandHelp.js +15 -0
- package/src/terminal/commandLlm.js +195 -0
- package/src/terminal/commandQuit.js +12 -0
- package/src/terminal/commandTest.js +21 -0
- package/src/terminal/index.js +23 -0
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Momo SDD - Spec Driven Development 工具
|
|
2
2
|
|
|
3
3
|
 | [](https://www.npmjs.com/package/momo-ai)
|
|
4
|
-
> For Rachel Momo
|
|
4
|
+
> For [Rachel Momo](https://www.weibo.com/maoxiaotong0216) / Serial Experiments Lain
|
|
5
5
|
|
|
6
6
|
## 1. 介绍
|
|
7
7
|
|
|
@@ -114,6 +114,38 @@ momo unlock # 解锁任务 -> 任务状态 = 待办
|
|
|
114
114
|
|
|
115
115
|
> 控制台开发中(Lain 模式)
|
|
116
116
|
|
|
117
|
+
```bash
|
|
118
|
+
# 两个命令都可以启动控制台
|
|
119
|
+
momo console # 启动 Lain 控制台 -> 交互式命令
|
|
120
|
+
lain # 启动 Lain 控制台 -> 交互式命令
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
打开控制台
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
[Momo AI] ----------------- Rachel Momo / AI工具项 ------------------
|
|
127
|
+
[Momo AI] 应用名称: Rachel Momo / SDD
|
|
128
|
+
[Momo AI] 工具主页: https://gitee.com/silentbalanceyh/r2mo-lain
|
|
129
|
+
[Momo AI] 工具版本: 1.0.6 ( Node >= 22.x )
|
|
130
|
+
[Momo AI]
|
|
131
|
+
[Momo AI] ----------------- AI 系统启动…… ----------------------------
|
|
132
|
+
|
|
133
|
+
================================================================================================
|
|
134
|
+
Momo AI / Lain Console
|
|
135
|
+
================================================================================================
|
|
136
|
+
|
|
137
|
+
欢迎使用 Momo AI / Lain 控制台!
|
|
138
|
+
这是一个交互式命令行界面。
|
|
139
|
+
|
|
140
|
+
可用命令:
|
|
141
|
+
help - 显示帮助信息
|
|
142
|
+
llm - 查看大模型配置信息
|
|
143
|
+
quit - 退出控制台
|
|
144
|
+
|
|
145
|
+
请在提示符后输入命令。
|
|
146
|
+
------------------------------------------------------------------------------------------------
|
|
147
|
+
```
|
|
148
|
+
|
|
117
149
|
<hr/>
|
|
118
150
|
|
|
119
151
|
## 3. 参考链接
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "momo-ai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Rachel Momo ( OpenSpec )",
|
|
5
5
|
"main": "src/momo.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"momo": "./src/momo.js"
|
|
7
|
+
"momo": "./src/momo.js",
|
|
8
|
+
"lain": "./src/lain.js"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"test": "src/index.test.js"
|
|
@@ -1,95 +1,102 @@
|
|
|
1
1
|
# AI Agents 集成说明
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
###
|
|
17
|
-
-
|
|
18
|
-
- 用途:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
3
|
+
本文档用于帮助 AI 编码助手理解项目结构和工作流程。通过遵循本文档中的规范,AI 可以更准确地协助开发工作。
|
|
4
|
+
|
|
5
|
+
## 项目工作流程
|
|
6
|
+
|
|
7
|
+
1. **需求分析** - 定义和分析业务需求
|
|
8
|
+
2. **任务规划** - 将需求分解为可执行的任务
|
|
9
|
+
3. **角色分配** - 为每个任务分配合适的 AI 或人类角色
|
|
10
|
+
4. **代码实现** - 执行具体任务并生成代码
|
|
11
|
+
5. **测试验证** - 验证实现是否满足需求
|
|
12
|
+
6. **归档提交** - 将完成的工作归档并提交
|
|
13
|
+
|
|
14
|
+
## 可用 Agents
|
|
15
|
+
|
|
16
|
+
### 领域建模师
|
|
17
|
+
- Arg Name: `momo-datamodel`
|
|
18
|
+
- 用途: 根据需求文档设计领域模型和数据结构
|
|
19
|
+
- id = momo-datamodel, uri = https://s.trae.ai/a/a9dcaa
|
|
20
|
+
|
|
21
|
+
### 系统需求分析师
|
|
22
|
+
- Arg Name: `momo-system-req`
|
|
23
|
+
- 用途: 分析系统级需求并生成技术规格
|
|
24
|
+
- id = momo-system-req, uri = https://s.trae.ai/a/d0c6b9
|
|
25
|
+
|
|
26
|
+
### 模块需求分析师
|
|
27
|
+
- Arg Name: `momo-module-req`
|
|
28
|
+
- 用途: 分析模块级需求并细化功能规格
|
|
29
|
+
- id = momo-module-req, uri = https://s.trae.ai/a/c4fc46
|
|
30
|
+
|
|
31
|
+
### 任务规划师
|
|
32
|
+
- Arg Name: `momo-task`
|
|
33
|
+
- 用途: 将需求分解为具体可执行的任务
|
|
34
|
+
- id = momo-task, uri = https://s.trae.ai/a/1fdc4e
|
|
35
|
+
|
|
36
|
+
## 项目目录结构
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
project/
|
|
40
|
+
├── .momo/
|
|
41
|
+
│ ├── advanced/
|
|
42
|
+
│ ├── prompt/
|
|
43
|
+
│ ├── scripts/
|
|
44
|
+
│ └── template/
|
|
45
|
+
├── integration/
|
|
46
|
+
├── source/
|
|
47
|
+
├── specification/
|
|
48
|
+
│ ├── .activities/
|
|
49
|
+
│ ├── .archives/
|
|
50
|
+
│ ├── actor/
|
|
51
|
+
│ ├── changes/
|
|
52
|
+
│ │ └── {change-id}/
|
|
53
|
+
│ │ ├── proposal.md
|
|
54
|
+
│ │ ├── tasks.md
|
|
55
|
+
│ │ └── tasks/
|
|
56
|
+
│ │ └── {task-id}.md
|
|
57
|
+
│ ├── project-model.md
|
|
58
|
+
│ ├── project.md
|
|
59
|
+
│ └── requirement.md
|
|
60
|
+
└── README.md
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## 关键文件说明
|
|
64
|
+
|
|
65
|
+
### specification/project.md
|
|
66
|
+
项目概述文件,包含项目基本信息、技术栈、架构风格等。
|
|
67
|
+
|
|
68
|
+
### specification/project-model.md
|
|
69
|
+
项目数据模型文件,包含核心领域模型和数据结构定义。
|
|
70
|
+
|
|
71
|
+
### specification/requirement.md
|
|
72
|
+
项目需求文件,包含用户群描述、场景描述、功能需求等。
|
|
73
|
+
|
|
74
|
+
### specification/changes/{change-id}/proposal.md
|
|
75
|
+
变更提案文件,描述需求变更的内容和影响范围。
|
|
76
|
+
|
|
77
|
+
### specification/changes/{change-id}/tasks.md
|
|
78
|
+
任务列表文件,包含为实现变更所需执行的具体任务。
|
|
79
|
+
|
|
80
|
+
### specification/changes/{change-id}/tasks/{task-id}.md
|
|
81
|
+
具体任务文件,包含任务的详细说明和执行指南。
|
|
82
|
+
|
|
83
|
+
## 工作流程规范
|
|
84
|
+
|
|
85
|
+
### 需求变更流程
|
|
86
|
+
1. 使用 `momo add -n "需求名称"` 创建新的需求变更
|
|
87
|
+
2. 使用 `momo plan -n "需求名称"` 生成开发计划
|
|
83
88
|
3. 使用 `momo run` 执行具体任务
|
|
84
|
-
4. 使用 `momo archive` 归档完成的需求
|
|
89
|
+
4. 使用 `momo archive -n "需求名称"` 归档完成的需求
|
|
85
90
|
|
|
86
91
|
### 角色管理流程
|
|
92
|
+
1. 使用 `momo actors` 查看所有已定义角色
|
|
93
|
+
2. 使用 `momo actor -a "角色名称"` 创建新角色
|
|
87
94
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
95
|
+
### 任务执行流程
|
|
96
|
+
1. 使用 `momo tasks` 查看所有待执行任务
|
|
97
|
+
2. 使用 `momo run -a {actor} -t {task}` 分配任务给角色执行
|
|
91
98
|
|
|
92
|
-
##
|
|
99
|
+
## 提示词规范
|
|
93
100
|
|
|
94
101
|
1. 所有提示词存储在 `.momo/prompt/` 目录中
|
|
95
102
|
2. 使用 `momo run` 命令时会自动将相关提示词复制到剪贴板
|
|
@@ -100,4 +107,4 @@
|
|
|
100
107
|
1. 请勿直接修改 `specification/changes/` 目录下的文件,应通过 `momo` 命令进行管理
|
|
101
108
|
2. 所有需求变更应遵循 OpenSpec 标准流程
|
|
102
109
|
3. 归档的需求存储在 `specification/.archives/` 目录中,可通过时间戳识别
|
|
103
|
-
4. 不同 AI 工具应使用对应的 `integration/` 子目录进行配置
|
|
110
|
+
4. 不同 AI 工具应使用对应的 `integration/` 子目录进行配置
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 执行 console 命令 - 启动交互式控制台
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const readline = require('readline');
|
|
6
|
+
const colors = require('colors');
|
|
7
|
+
const Ec = require('../epic');
|
|
8
|
+
|
|
9
|
+
// 显示欢迎界面和菜单
|
|
10
|
+
const showMenu = () => {
|
|
11
|
+
// 清屏
|
|
12
|
+
process.stdout.write('\x1Bc');
|
|
13
|
+
|
|
14
|
+
// 显示标准头部信息
|
|
15
|
+
Ec.executeHeader("Rachel Momo / SDD");
|
|
16
|
+
|
|
17
|
+
// 使用96个字符宽度
|
|
18
|
+
const width = 96;
|
|
19
|
+
const headerBorder = '='.repeat(width).blue;
|
|
20
|
+
const footerBorder = '-'.repeat(width).blue;
|
|
21
|
+
|
|
22
|
+
console.log('');
|
|
23
|
+
console.log(headerBorder);
|
|
24
|
+
const title = 'Momo AI / Lain Console';
|
|
25
|
+
const padding = ' '.repeat(Math.floor((width - title.length) / 2) - 1);
|
|
26
|
+
console.log(`${padding}${title}`.bold.brightCyan);
|
|
27
|
+
console.log(headerBorder);
|
|
28
|
+
|
|
29
|
+
console.log('');
|
|
30
|
+
console.log('欢迎使用 Momo AI / Lain 控制台!'.green);
|
|
31
|
+
console.log('这是一个交互式命令行界面。'.yellow);
|
|
32
|
+
console.log('');
|
|
33
|
+
|
|
34
|
+
console.log('可用命令:'.bold);
|
|
35
|
+
console.log(' help - 显示帮助信息'.white);
|
|
36
|
+
console.log(' exit - 退出控制台'.white);
|
|
37
|
+
console.log(' quit - 退出控制台'.white);
|
|
38
|
+
console.log('');
|
|
39
|
+
|
|
40
|
+
console.log('请在提示符后输入命令。'.gray);
|
|
41
|
+
console.log(footerBorder);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// 处理用户输入
|
|
45
|
+
const handleInput = (input) => {
|
|
46
|
+
const command = input.trim().toLowerCase();
|
|
47
|
+
|
|
48
|
+
switch (command) {
|
|
49
|
+
case '':
|
|
50
|
+
// 空命令,不处理
|
|
51
|
+
break;
|
|
52
|
+
case 'help':
|
|
53
|
+
console.log('');
|
|
54
|
+
console.log('帮助信息:'.bold.brightYellow);
|
|
55
|
+
console.log(' help - 显示此帮助信息'.white);
|
|
56
|
+
console.log(' exit - 退出控制台'.white);
|
|
57
|
+
console.log(' quit - 退出控制台'.white);
|
|
58
|
+
console.log('');
|
|
59
|
+
break;
|
|
60
|
+
case 'exit':
|
|
61
|
+
case 'quit':
|
|
62
|
+
console.log('');
|
|
63
|
+
console.log('感谢使用 Momo AI / Lain 控制台,再见!'.brightGreen);
|
|
64
|
+
console.log('');
|
|
65
|
+
process.exit(0);
|
|
66
|
+
break;
|
|
67
|
+
default:
|
|
68
|
+
console.log('');
|
|
69
|
+
console.log(`未知命令: ${command}`.brightRed);
|
|
70
|
+
console.log('输入 "help" 查看可用命令。'.yellow);
|
|
71
|
+
console.log('');
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// 主函数
|
|
76
|
+
const executeConsole = async () => {
|
|
77
|
+
showMenu();
|
|
78
|
+
|
|
79
|
+
// 先关闭全局的 readline 接口,避免冲突
|
|
80
|
+
try {
|
|
81
|
+
const globalRl = require('../epic/momo.fn.log');
|
|
82
|
+
if (globalRl.askClose) {
|
|
83
|
+
globalRl.askClose();
|
|
84
|
+
}
|
|
85
|
+
} catch (e) {
|
|
86
|
+
// 忽略错误
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 创建 readline 接口
|
|
90
|
+
const rl = readline.createInterface({
|
|
91
|
+
input: process.stdin,
|
|
92
|
+
output: process.stdout,
|
|
93
|
+
prompt: '[Lain AI] > '.cyan.bold
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
rl.prompt();
|
|
97
|
+
|
|
98
|
+
rl.on('line', (line) => {
|
|
99
|
+
const input = line.trim();
|
|
100
|
+
handleInput(input);
|
|
101
|
+
rl.prompt();
|
|
102
|
+
}).on('close', () => {
|
|
103
|
+
console.log('\n' + '感谢使用 Momo AI / Lain 控制台,再见!'.brightGreen + '\n');
|
|
104
|
+
process.exit(0);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// 处理 Ctrl+C
|
|
108
|
+
process.on('SIGINT', () => {
|
|
109
|
+
console.log('\n\n' + '感谢使用 Momo AI / Lain 控制台,再见!'.brightGreen + '\n');
|
|
110
|
+
rl.close();
|
|
111
|
+
process.exit(0);
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
module.exports = executeConsole;
|
|
@@ -150,7 +150,21 @@ const _ioFile = async (baseDir) => {
|
|
|
150
150
|
"integration/windsurf/",
|
|
151
151
|
"integration/github/",
|
|
152
152
|
"integration/claude-code/",
|
|
153
|
-
"integration/chatgpt/"
|
|
153
|
+
"integration/chatgpt/",
|
|
154
|
+
// 新增的 integration 目录(使用小写)
|
|
155
|
+
"integration/auggie/",
|
|
156
|
+
"integration/cline/",
|
|
157
|
+
"integration/roocode/",
|
|
158
|
+
"integration/codebuddy/",
|
|
159
|
+
"integration/costrict/",
|
|
160
|
+
"integration/crush/",
|
|
161
|
+
"integration/factory/",
|
|
162
|
+
"integration/gemini/",
|
|
163
|
+
"integration/opencode/",
|
|
164
|
+
"integration/kilo/",
|
|
165
|
+
"integration/codex/",
|
|
166
|
+
"integration/amazonq/",
|
|
167
|
+
"integration/qwen/"
|
|
154
168
|
];
|
|
155
169
|
|
|
156
170
|
// 为每个目录创建一个空的 .gitkeep 文件,确保目录被git跟踪
|
|
@@ -168,6 +182,70 @@ const _ioFile = async (baseDir) => {
|
|
|
168
182
|
}
|
|
169
183
|
}
|
|
170
184
|
|
|
185
|
+
// 为每个 integration 目录创建 config.json 文件
|
|
186
|
+
const integrationDirs = [
|
|
187
|
+
"integration/openspec/",
|
|
188
|
+
"integration/spec-kit/",
|
|
189
|
+
"integration/kiro/",
|
|
190
|
+
"integration/trea/",
|
|
191
|
+
"integration/cursor/",
|
|
192
|
+
"integration/lingma/",
|
|
193
|
+
"integration/qoder/",
|
|
194
|
+
"integration/windsurf/",
|
|
195
|
+
"integration/github/",
|
|
196
|
+
"integration/claude-code/",
|
|
197
|
+
"integration/chatgpt/",
|
|
198
|
+
// 新增的 integration 目录(使用小写)
|
|
199
|
+
"integration/auggie/",
|
|
200
|
+
"integration/cline/",
|
|
201
|
+
"integration/roocode/",
|
|
202
|
+
"integration/codebuddy/",
|
|
203
|
+
"integration/costrict/",
|
|
204
|
+
"integration/crush/",
|
|
205
|
+
"integration/factory/",
|
|
206
|
+
"integration/gemini/",
|
|
207
|
+
"integration/opencode/",
|
|
208
|
+
"integration/kilo/",
|
|
209
|
+
"integration/codex/",
|
|
210
|
+
"integration/amazonq/",
|
|
211
|
+
"integration/qwen/"
|
|
212
|
+
];
|
|
213
|
+
|
|
214
|
+
// 通用的 LLM 配置模板
|
|
215
|
+
const configTemplate = {
|
|
216
|
+
llm: "", // LLM 类型标识符
|
|
217
|
+
token: "", // API 访问令牌
|
|
218
|
+
baseUrl: "", // API 基础 URL(可选)
|
|
219
|
+
model: "", // 模型名称(可选)
|
|
220
|
+
temperature: 0.7, // 温度参数(可选)
|
|
221
|
+
maxTokens: 2048, // 最大令牌数(可选)
|
|
222
|
+
topP: 1.0, // Top-P 参数(可选)
|
|
223
|
+
frequencyPenalty: 0, // 频率惩罚(可选)
|
|
224
|
+
presencePenalty: 0 // 存在惩罚(可选)
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// 为每个 integration 目录创建 config.json
|
|
228
|
+
for (const dir of integrationDirs) {
|
|
229
|
+
const configPath = path.resolve(baseDir, dir, "config.json");
|
|
230
|
+
// 确保目录存在
|
|
231
|
+
const dirPath = path.dirname(configPath);
|
|
232
|
+
if (!fs.existsSync(dirPath)) {
|
|
233
|
+
await fsAsync.mkdir(dirPath, {recursive: true});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// 如果文件不存在或用户选择覆盖,则创建 config.json
|
|
237
|
+
if (!fs.existsSync(configPath) || shouldOverwrite) {
|
|
238
|
+
// 根据目录名设置 llm 字段
|
|
239
|
+
const dirName = path.basename(dir);
|
|
240
|
+
const config = {...configTemplate, llm: dirName};
|
|
241
|
+
|
|
242
|
+
Ec.waiting("创建配置文件:" + configPath);
|
|
243
|
+
await fsAsync.writeFile(configPath, JSON.stringify(config, null, 4));
|
|
244
|
+
} else {
|
|
245
|
+
Ec.waiting("跳过文件:" + configPath);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
171
249
|
// 拷贝模板文件
|
|
172
250
|
for (const file of templateFiles) {
|
|
173
251
|
const sourcePath = path.resolve(templateDir, file.source);
|
|
@@ -299,6 +377,19 @@ const _ioFile = async (baseDir) => {
|
|
|
299
377
|
* github/ Github Copilot 目录
|
|
300
378
|
* claude-code/ Claude Code 目录
|
|
301
379
|
* chatgpt/ ChatGPT 目录
|
|
380
|
+
* auggie/ Auggie (Augment CLI) 目录
|
|
381
|
+
* cline/ Cline 目录
|
|
382
|
+
* roocode/ RooCode 目录
|
|
383
|
+
* codebuddy/ CodeBuddy Code (CLI) 目录
|
|
384
|
+
* costrict/ CoStrict 目录
|
|
385
|
+
* crush/ Crush 目录
|
|
386
|
+
* factory/ Factory Droid 目录
|
|
387
|
+
* gemini/ Gemini CLI 目录
|
|
388
|
+
* opencode/ OpenCode 目录
|
|
389
|
+
* kilo/ Kilo Code 目录
|
|
390
|
+
* codex/ Codex 目录
|
|
391
|
+
* amazonq/ Amazon Q Developer 目录
|
|
392
|
+
* qwen/ Qwen Code 目录
|
|
302
393
|
* @param options
|
|
303
394
|
*/
|
|
304
395
|
module.exports = (options) => {
|
package/src/executor/index.js
CHANGED
|
@@ -17,6 +17,7 @@ const executeUnlock = require('./executeUnlock');
|
|
|
17
17
|
const executeProject = require('./executeProject');
|
|
18
18
|
const executeAgentCfg = require('./executeAgentCfg');
|
|
19
19
|
const executeAgent = require('./executeAgent');
|
|
20
|
+
const executeConsole = require('./executeConsole');
|
|
20
21
|
|
|
21
22
|
const exported = {
|
|
22
23
|
executeHelp,
|
|
@@ -37,6 +38,7 @@ const exported = {
|
|
|
37
38
|
executeUnlock,
|
|
38
39
|
executeProject,
|
|
39
40
|
executeAgentCfg,
|
|
40
|
-
executeAgent
|
|
41
|
+
executeAgent,
|
|
42
|
+
executeConsole
|
|
41
43
|
};
|
|
42
44
|
module.exports = exported;
|
package/src/lain.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lain Console - 独立的交互式控制台入口
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const readline = require('readline');
|
|
8
|
+
const colors = require('colors');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const terminalCommands = require('./terminal');
|
|
12
|
+
|
|
13
|
+
// 设置颜色主题
|
|
14
|
+
colors.setTheme({
|
|
15
|
+
silly: 'rainbow',
|
|
16
|
+
input: 'grey',
|
|
17
|
+
verbose: 'cyan',
|
|
18
|
+
prompt: 'red',
|
|
19
|
+
info: 'green',
|
|
20
|
+
data: 'blue',
|
|
21
|
+
help: 'cyan',
|
|
22
|
+
warn: 'yellow',
|
|
23
|
+
debug: 'magenta',
|
|
24
|
+
error: 'red'
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// 显示欢迎界面和菜单
|
|
28
|
+
const showMenu = () => {
|
|
29
|
+
// 清屏
|
|
30
|
+
process.stdout.write('\x1Bc');
|
|
31
|
+
|
|
32
|
+
// 显示标准头部信息
|
|
33
|
+
showHeader();
|
|
34
|
+
|
|
35
|
+
// 使用96个字符宽度
|
|
36
|
+
const width = 96;
|
|
37
|
+
const headerBorder = '='.repeat(width).blue;
|
|
38
|
+
const footerBorder = '-'.repeat(width).blue;
|
|
39
|
+
|
|
40
|
+
console.log('');
|
|
41
|
+
console.log(headerBorder);
|
|
42
|
+
const title = 'Momo AI / Lain Console';
|
|
43
|
+
const padding = ' '.repeat(Math.floor((width - title.length) / 2) - 1);
|
|
44
|
+
console.log(`${padding}${title}`.bold.brightCyan);
|
|
45
|
+
console.log(headerBorder);
|
|
46
|
+
|
|
47
|
+
console.log('');
|
|
48
|
+
console.log('欢迎使用 Momo AI / Lain 控制台!'.green);
|
|
49
|
+
console.log('这是一个交互式命令行界面。'.yellow);
|
|
50
|
+
console.log('');
|
|
51
|
+
|
|
52
|
+
console.log('可用命令:'.bold);
|
|
53
|
+
console.log(' help - 显示帮助信息'.white);
|
|
54
|
+
console.log(' llm - 查看大模型配置信息'.white);
|
|
55
|
+
console.log(' quit - 退出控制台'.white);
|
|
56
|
+
console.log('');
|
|
57
|
+
|
|
58
|
+
console.log('请在提示符后输入命令。'.gray);
|
|
59
|
+
console.log(footerBorder);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// 显示标准头部信息
|
|
63
|
+
const showHeader = () => {
|
|
64
|
+
const appInfo = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf8'));
|
|
65
|
+
|
|
66
|
+
console.log(`[Momo AI]`.green.bold + ` ----------------- Rachel Momo / AI工具项 ------------------`.rainbow);
|
|
67
|
+
console.log(`[Momo AI]`.green.bold + ' 应用名称: '.bold + 'Rachel Momo / SDD');
|
|
68
|
+
console.log(`[Momo AI]`.green.bold + ' 工具主页: '.bold + appInfo.homepage.blue);
|
|
69
|
+
console.log(`[Momo AI]`.green.bold + ` 工具版本: ` + `${appInfo.version}`.red + ' ' + `( Node >= 22.x )`.yellow);
|
|
70
|
+
console.log(`[Momo AI]`.green.bold);
|
|
71
|
+
console.log(`[Momo AI]`.green.bold + ` ----------------- AI 系统启动…… ----------------------------`.rainbow);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// 处理用户输入
|
|
75
|
+
const handleInput = (input, commands) => {
|
|
76
|
+
const command = input.trim().toLowerCase();
|
|
77
|
+
|
|
78
|
+
// 创建命令上下文
|
|
79
|
+
const context = {
|
|
80
|
+
commands: commands
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
switch (command) {
|
|
84
|
+
case '':
|
|
85
|
+
// 空命令,不处理
|
|
86
|
+
break;
|
|
87
|
+
case 'help':
|
|
88
|
+
commands.help(context);
|
|
89
|
+
break;
|
|
90
|
+
case 'test':
|
|
91
|
+
commands.test(context);
|
|
92
|
+
break;
|
|
93
|
+
case 'llm':
|
|
94
|
+
commands.llm(context);
|
|
95
|
+
break;
|
|
96
|
+
case 'quit':
|
|
97
|
+
commands.quit(context);
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
console.log('');
|
|
101
|
+
console.log(`未知命令: ${command}`.brightRed);
|
|
102
|
+
console.log('输入 "help" 查看可用命令。'.yellow);
|
|
103
|
+
console.log('');
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// 主函数
|
|
108
|
+
const main = async () => {
|
|
109
|
+
showMenu();
|
|
110
|
+
|
|
111
|
+
// 创建 readline 接口
|
|
112
|
+
const rl = readline.createInterface({
|
|
113
|
+
input: process.stdin,
|
|
114
|
+
output: process.stdout,
|
|
115
|
+
prompt: '[Lain AI] > '.cyan.bold
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
rl.prompt();
|
|
119
|
+
|
|
120
|
+
rl.on('line', (line) => {
|
|
121
|
+
const input = line.trim();
|
|
122
|
+
handleInput(input, terminalCommands);
|
|
123
|
+
rl.prompt();
|
|
124
|
+
}).on('close', () => {
|
|
125
|
+
console.log('\n' + '感谢使用 Momo AI / Lain 控制台,再见!'.brightGreen + '\n');
|
|
126
|
+
process.exit(0);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// 处理 Ctrl+C
|
|
130
|
+
process.on('SIGINT', () => {
|
|
131
|
+
console.log('\n\n' + '感谢使用 Momo AI / Lain 控制台,再见!'.brightGreen + '\n');
|
|
132
|
+
rl.close();
|
|
133
|
+
process.exit(0);
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// 启动程序
|
|
138
|
+
main().catch(err => {
|
|
139
|
+
console.error('启动控制台时出错:', err);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Help command implementation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const helpCommand = (context) => {
|
|
6
|
+
console.log('');
|
|
7
|
+
console.log('帮助信息:'.bold.brightYellow);
|
|
8
|
+
|
|
9
|
+
console.log(' help - 显示此帮助信息'.white);
|
|
10
|
+
console.log(' llm - 查看大模型配置信息'.white);
|
|
11
|
+
console.log(' quit - 退出控制台'.white);
|
|
12
|
+
console.log('');
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
module.exports = helpCommand;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM command implementation
|
|
3
|
+
*
|
|
4
|
+
* 查看所有大模型配置信息,包括名称、token、主页等
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const llmCommand = (context) => {
|
|
11
|
+
console.log('');
|
|
12
|
+
console.log('大模型配置信息:'.bold.brightCyan);
|
|
13
|
+
|
|
14
|
+
// 定义所有支持的 LLM 及其主页信息
|
|
15
|
+
const llmInfo = {
|
|
16
|
+
'openspec': { name: 'OpenSpec', homepage: 'https://openspec.dev/', isLLM: false },
|
|
17
|
+
'spec-kit': { name: 'SpecKit', homepage: 'https://github.com/github/spec-kit', isLLM: false },
|
|
18
|
+
'kiro': { name: 'Kiro', homepage: 'https://kiro.dev/', isLLM: false },
|
|
19
|
+
'trea': { name: 'Trea', homepage: 'https://www.trae.ai/', isLLM: false },
|
|
20
|
+
'cursor': { name: 'Cursor', homepage: 'https://cursor.com/', isLLM: false },
|
|
21
|
+
'lingma': { name: 'Lingma', homepage: 'https://lingma.aliyun.com/', isLLM: false },
|
|
22
|
+
'qoder': { name: 'Qoder', homepage: 'https://qoder.com/', isLLM: false },
|
|
23
|
+
'windsurf': { name: 'WindSurf', homepage: 'https://windsurf.com/', isLLM: false },
|
|
24
|
+
'github': { name: 'GitHub Copilot', homepage: 'https://github.com/features/copilot', isLLM: false },
|
|
25
|
+
'claude-code': { name: 'Claude Code', homepage: 'https://claude.ai/code', isLLM: true },
|
|
26
|
+
'chatgpt': { name: 'ChatGPT', homepage: 'https://chatgpt.com/', isLLM: true },
|
|
27
|
+
'auggie': { name: 'Auggie', homepage: 'https://auggie.dev', isLLM: false },
|
|
28
|
+
'cline': { name: 'Cline', homepage: 'https://cline.bot/', isLLM: false },
|
|
29
|
+
'roocode': { name: 'RooCode', homepage: 'https://roocode.com', isLLM: false },
|
|
30
|
+
'codebuddy': { name: 'CodeBuddy', homepage: 'https://codebuddy.ai', isLLM: false },
|
|
31
|
+
'crush': { name: 'Crush', homepage: 'https://github.com/charmbracelet/crush', isLLM: false },
|
|
32
|
+
'factory': { name: 'Factory Droid', homepage: 'https://factory.ai/', isLLM: false },
|
|
33
|
+
'gemini': { name: 'Gemini', homepage: 'https://gemini.google.com', isLLM: true },
|
|
34
|
+
'opencode': { name: 'OpenCode', homepage: 'https://opencode.ai', isLLM: false },
|
|
35
|
+
'kilo': { name: 'Kilo Code', homepage: 'https://kilo.ai/', isLLM: false },
|
|
36
|
+
'codex': { name: 'Codex', homepage: 'https://chatgpt.com/codex', isLLM: false },
|
|
37
|
+
'qwen': { name: 'Qwen Code', homepage: 'https://chat.qwen.ai/', isLLM: true },
|
|
38
|
+
'deepseek': { name: 'Deep Seek', homepage: 'https://www.deepseek.com/', isLLM: true },
|
|
39
|
+
'silicon': { name: 'Silicon Flow', homepage: 'https://www.siliconflow.com/', isLLM: false },
|
|
40
|
+
'kimi': { name: 'Kimi', homepage: 'https://www.kimi.com/', isLLM: false },
|
|
41
|
+
'grok': { name: 'Grok', homepage: 'https://grok.com/', isLLM: true }
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// 获取当前项目中的 integration 目录
|
|
45
|
+
const integrationBaseDir = path.resolve(process.cwd(), 'integration');
|
|
46
|
+
|
|
47
|
+
// 确保 integration 目录存在
|
|
48
|
+
if (!fs.existsSync(integrationBaseDir)) {
|
|
49
|
+
try {
|
|
50
|
+
fs.mkdirSync(integrationBaseDir, { recursive: true });
|
|
51
|
+
} catch (e) {
|
|
52
|
+
// 创建目录出错
|
|
53
|
+
console.log(' 无法创建 integration 目录'.brightRed);
|
|
54
|
+
console.log('');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 获取所有 integration 目录
|
|
60
|
+
let integrationDirs = [];
|
|
61
|
+
try {
|
|
62
|
+
if (fs.existsSync(integrationBaseDir)) {
|
|
63
|
+
integrationDirs = fs.readdirSync(integrationBaseDir)
|
|
64
|
+
.filter(dir => fs.statSync(path.join(integrationBaseDir, dir)).isDirectory());
|
|
65
|
+
}
|
|
66
|
+
} catch (e) {
|
|
67
|
+
// 读取目录出错
|
|
68
|
+
console.log(' 无法读取 integration 目录'.brightRed);
|
|
69
|
+
console.log('');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 构建所有支持模型的数据
|
|
74
|
+
const allModels = Object.keys(llmInfo).map(key => {
|
|
75
|
+
const info = llmInfo[key];
|
|
76
|
+
return {
|
|
77
|
+
dir: key,
|
|
78
|
+
name: info.name,
|
|
79
|
+
homepage: info.homepage,
|
|
80
|
+
isLLM: info.isLLM
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// 获取每个目录的详细信息并排序
|
|
85
|
+
const llmData = allModels.map(model => {
|
|
86
|
+
let name = model.name;
|
|
87
|
+
let homepage = model.homepage;
|
|
88
|
+
let configured = ''; // 默认为空
|
|
89
|
+
let isLLM = model.isLLM;
|
|
90
|
+
|
|
91
|
+
// 尝试读取对应 integration 目录中的 config.json
|
|
92
|
+
try {
|
|
93
|
+
const configPath = path.resolve(integrationBaseDir, model.dir, 'config.json');
|
|
94
|
+
if (fs.existsSync(configPath)) {
|
|
95
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
96
|
+
const token = config.token || '-';
|
|
97
|
+
|
|
98
|
+
// 处理 token 显示,避免显示完整 token
|
|
99
|
+
if (token !== '-' && token.length > 0) {
|
|
100
|
+
configured = '✅'; // 有token则标记为已配置
|
|
101
|
+
} else if (isLLM) {
|
|
102
|
+
configured = '❌'; // 是LLM但没有token
|
|
103
|
+
}
|
|
104
|
+
} else if (isLLM) {
|
|
105
|
+
configured = '❌'; // 是LLM但没有配置文件
|
|
106
|
+
|
|
107
|
+
// 自动创建目录和基本配置文件
|
|
108
|
+
const modelDir = path.resolve(integrationBaseDir, model.dir);
|
|
109
|
+
if (!fs.existsSync(modelDir)) {
|
|
110
|
+
fs.mkdirSync(modelDir, { recursive: true });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 创建默认配置文件
|
|
114
|
+
const defaultConfig = {
|
|
115
|
+
llm: model.dir,
|
|
116
|
+
token: "",
|
|
117
|
+
baseUrl: "",
|
|
118
|
+
model: "",
|
|
119
|
+
temperature: 0.7,
|
|
120
|
+
maxTokens: 2048,
|
|
121
|
+
topP: 1,
|
|
122
|
+
frequencyPenalty: 0,
|
|
123
|
+
presencePenalty: 0
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const configPath = path.resolve(modelDir, 'config.json');
|
|
127
|
+
if (!fs.existsSync(configPath)) {
|
|
128
|
+
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
|
|
129
|
+
}
|
|
130
|
+
} else if (!isLLM) {
|
|
131
|
+
// 对于非LLM,如果没有配置文件也创建一个
|
|
132
|
+
const modelDir = path.resolve(integrationBaseDir, model.dir);
|
|
133
|
+
if (!fs.existsSync(modelDir)) {
|
|
134
|
+
fs.mkdirSync(modelDir, { recursive: true });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 创建默认配置文件
|
|
138
|
+
const defaultConfig = {
|
|
139
|
+
llm: model.dir,
|
|
140
|
+
token: "",
|
|
141
|
+
baseUrl: "",
|
|
142
|
+
model: "",
|
|
143
|
+
temperature: 0.7,
|
|
144
|
+
maxTokens: 2048,
|
|
145
|
+
topP: 1,
|
|
146
|
+
frequencyPenalty: 0,
|
|
147
|
+
presencePenalty: 0
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const configPath = path.resolve(modelDir, 'config.json');
|
|
151
|
+
if (!fs.existsSync(configPath)) {
|
|
152
|
+
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} catch (e) {
|
|
156
|
+
// 读取配置文件出错
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 如果是LLM,在名称后添加(LLM)和emoji
|
|
160
|
+
if (isLLM) {
|
|
161
|
+
name = name + ' (LLM) 🌟';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
dir: model.dir,
|
|
166
|
+
name: name,
|
|
167
|
+
homepage: homepage,
|
|
168
|
+
configured: configured,
|
|
169
|
+
isLLM: isLLM
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// 按类型排序,LLM优先,然后按名称排序
|
|
174
|
+
llmData.sort((a, b) => {
|
|
175
|
+
// 首先按类型排序(LLM在前)
|
|
176
|
+
if (a.isLLM && !b.isLLM) return -1;
|
|
177
|
+
if (!a.isLLM && b.isLLM) return 1;
|
|
178
|
+
// 然后按名称排序
|
|
179
|
+
return a.name.localeCompare(b.name);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// 表格头部,调整列宽
|
|
183
|
+
console.log(' ' + '名称'.padEnd(25) + '标识符'.padEnd(15) + '主页'.padEnd(45) + '状态');
|
|
184
|
+
console.log(' ' + '-'.repeat(85));
|
|
185
|
+
|
|
186
|
+
// 显示每个 LLM 的信息
|
|
187
|
+
llmData.forEach(item => {
|
|
188
|
+
// 输出格式:名称 标识符 主页 状态
|
|
189
|
+
console.log(' ' + item.name.padEnd(25) + item.dir.padEnd(15) + item.homepage.padEnd(45) + item.configured);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
console.log('');
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
module.exports = llmCommand;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test command implementation
|
|
3
|
+
*
|
|
4
|
+
* 这是一个示例测试命令,用于演示如何添加新命令
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const testCommand = (context) => {
|
|
8
|
+
console.log('');
|
|
9
|
+
console.log('测试命令执行结果:'.bold.brightCyan);
|
|
10
|
+
console.log(' 这是一个测试命令的输出'.white);
|
|
11
|
+
console.log(' 当前时间: ' + new Date().toLocaleString().yellow);
|
|
12
|
+
console.log(' 您可以在此处添加任何测试逻辑'.gray);
|
|
13
|
+
console.log('');
|
|
14
|
+
|
|
15
|
+
// 示例:访问上下文中的信息
|
|
16
|
+
const commandCount = Object.keys(context.commands).length;
|
|
17
|
+
console.log(` 当前系统中可用命令数量: ${commandCount.toString().green}`);
|
|
18
|
+
console.log('');
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
module.exports = testCommand;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal Commands Index
|
|
3
|
+
*
|
|
4
|
+
* 所有终端命令的入口文件
|
|
5
|
+
* 除 help 和 quit 命令外,所有新追加的命令都应在此处导出
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// 基础命令
|
|
9
|
+
const helpCommand = require('./commandHelp');
|
|
10
|
+
const quitCommand = require('./commandQuit');
|
|
11
|
+
|
|
12
|
+
// 在此处添加新命令的导入
|
|
13
|
+
const llmCommand = require('./commandLlm');
|
|
14
|
+
|
|
15
|
+
// 导出所有命令
|
|
16
|
+
module.exports = {
|
|
17
|
+
// 基础命令
|
|
18
|
+
help: helpCommand,
|
|
19
|
+
quit: quitCommand,
|
|
20
|
+
|
|
21
|
+
// 在此处添加新命令的导出
|
|
22
|
+
llm: llmCommand
|
|
23
|
+
};
|