momo-ai 1.0.21 → 1.0.22
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/r2mo-rad-lain/SKILL.md +63 -374
- package/.trae/skills/algorithmic-art/LICENSE.txt +202 -0
- package/.trae/skills/algorithmic-art/SKILL.md +405 -0
- package/.trae/skills/algorithmic-art/templates/generator_template.js +223 -0
- package/.trae/skills/algorithmic-art/templates/viewer.html +599 -0
- package/.trae/skills/doc-coauthoring/SKILL.md +375 -0
- package/.trae/skills/frontend-design/LICENSE.txt +177 -0
- package/.trae/skills/frontend-design/SKILL.md +42 -0
- package/.trae/skills/r2mo-rad-lain/SKILL.md +101 -0
- package/README.md +9 -32
- package/docs/images/r2mo-lain.png +0 -0
- package/package.json +11 -11
- package/skills/r2mo-rad-domain/SKILL.md +70 -0
- package/src/_skill/repositories.json +9 -3
- package/src/_template/LAIN/.obsidian/app.json +1 -0
- package/src/_template/LAIN/.obsidian/appearance.json +10 -0
- package/src/_template/LAIN/.obsidian/community-plugins.json +7 -0
- package/src/_template/LAIN/.obsidian/core-plugins.json +33 -0
- package/src/_template/LAIN/.obsidian/plugins/dataview/main.js +20876 -0
- package/src/_template/LAIN/.obsidian/plugins/dataview/manifest.json +11 -0
- package/src/_template/LAIN/.obsidian/plugins/dataview/styles.css +141 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/data.json +815 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/main.js +10 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/manifest.json +12 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/styles.css +1 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-kanban/main.js +153 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-kanban/manifest.json +11 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-kanban/styles.css +1 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-plantuml/main.js +7732 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-plantuml/manifest.json +10 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-plantuml/styles.css +38 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-tasks-plugin/main.js +504 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-tasks-plugin/manifest.json +12 -0
- package/src/_template/LAIN/.obsidian/plugins/obsidian-tasks-plugin/styles.css +1 -0
- package/src/_template/LAIN/.obsidian/snippets/body-font.css +27 -0
- package/src/_template/LAIN/.obsidian/themes/Primary/manifest.json +9 -0
- package/src/_template/LAIN/.obsidian/themes/Primary/theme.css +3878 -0
- package/src/_template/LAIN/.obsidian/themes/Retro Windows/manifest.json +7 -0
- package/src/_template/LAIN/.obsidian/themes/Retro Windows/theme.css +582 -0
- package/src/_template/LAIN/.obsidian/themes/RetroOS 98/manifest.json +9 -0
- package/src/_template/LAIN/.obsidian/themes/RetroOS 98/theme.css +2566 -0
- package/src/_template/LAIN/.obsidian/types.json +28 -0
- package/src/_template/LAIN/.obsidian/workspace.json +184 -0
- package/src/_template/LAIN/AGENTS.md +170 -16
- package/src/_template/R2MO/domain-enhance.md +10 -0
- package/src/commander/app.json +13 -0
- package/src/commander/apply.json +13 -0
- package/src/commander/ask.json +6 -0
- package/src/commander/docs.json +13 -0
- package/src/commander/domain.json +19 -0
- package/src/commander/init.json +1 -1
- package/src/commander/mmr0.json +6 -0
- package/src/commander/mmr2.json +6 -0
- package/src/executor/executeApp.js +133 -0
- package/src/executor/{executeSkills.js → executeApply.js} +166 -302
- package/src/executor/executeAsk.js +274 -0
- package/src/executor/executeDocs.js +498 -0
- package/src/executor/executeDomain.js +293 -0
- package/src/executor/executeInit.js +159 -383
- package/src/executor/executeMcp.js +74 -1
- package/src/executor/executeMmr0.js +488 -0
- package/src/executor/executeMmr2.js +880 -0
- package/src/executor/index.js +15 -3
- package/src/python/r2mo_proto.py +418 -0
- package/src/python/r2mo_proto_database.py +369 -0
- package/src/python/r2mo_proto_domain.py +458 -0
- package/src/utils/momo-menu.js +43 -13
- package/.claude/skills/r2mo-rad-lain/PROMPT.md +0 -281
- package/.claude/skills/r2mo-rad-lain/README.md +0 -192
- package/.claude/skills/r2mo-rad-lain/examples/argument-parsing.js +0 -154
- package/.claude/skills/r2mo-rad-lain/examples/file-operations.js +0 -182
- package/.claude/skills/r2mo-rad-lain/file-utils-api.md +0 -281
- package/.claude/skills/r2mo-rad-lain/menu-api.md +0 -187
- package/.claude/skills/r2mo-rad-lain/scripts/file-utils.js +0 -223
- package/.claude/skills/r2mo-rad-lain/scripts/menu.js +0 -289
- package/.claude/skills/r2mo-rad-lain/scripts/yaml-parser.js +0 -209
- package/.claude/skills/r2mo-rad-lain/templates/command.json.template +0 -13
- package/.claude/skills/r2mo-rad-lain/templates/executor.js.template +0 -32
- package/.claude/skills/r2mo-rad-lain/templates/interactive-menu.js.template +0 -221
- package/src/_template/LAIN/.momo/advanced/actor.md +0 -42
- package/src/_template/LAIN/.momo/advanced/refer.json +0 -46
- package/src/_template/LAIN/.momo/scripts/submodule-clean.sh +0 -56
- package/src/_template/LAIN/changes/proposal.md +0 -39
- package/src/_template/LAIN/changes/tasks/task-detail.md +0 -45
- package/src/_template/LAIN/changes/tasks.md +0 -49
- package/src/_template/LAIN/execute/admin-n-f-dashboard.md +0 -53
- package/src/_template/LAIN/execute/admin-n-f-form.md +0 -51
- package/src/_template/LAIN/execute/admin-n-f-home.md +0 -49
- package/src/_template/LAIN/execute/admin-n-f-list.md +0 -52
- package/src/_template/LAIN/execute/admin-n-f-login.md +0 -56
- package/src/_template/LAIN/specification/project-model.md +0 -13
- package/src/_template/LAIN/specification/project.md +0 -73
- package/src/_template/LAIN/specification/requirement.md +0 -25
- package/src/commander/skills.json +0 -20
|
@@ -2,57 +2,32 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const Ec = require('../epic');
|
|
4
4
|
const fsAsync = require("fs").promises;
|
|
5
|
+
const { copyDir } = require('../utils/momo-file-utils');
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* 创建目录结构
|
|
9
|
+
* @param {string} baseDir 基础目录
|
|
10
|
+
*/
|
|
6
11
|
const _ioDirectory = async (baseDir) => {
|
|
7
12
|
const folders = [
|
|
8
|
-
".
|
|
9
|
-
".
|
|
10
|
-
".momo/advanced/",
|
|
11
|
-
".momo/scripts/",
|
|
12
|
-
".momo/specification/",
|
|
13
|
-
".momo/specification/changes/",
|
|
14
|
-
".momo/specification/actor/",
|
|
15
|
-
".momo/specification/.archives/",
|
|
16
|
-
".momo/specification/.activities/",
|
|
17
|
-
".momo/source/",
|
|
18
|
-
".momo/integration/",
|
|
19
|
-
".momo/integration/openspec/",
|
|
20
|
-
".momo/integration/spec-kit/",
|
|
21
|
-
".momo/integration/kiro/",
|
|
22
|
-
".momo/integration/trea/",
|
|
23
|
-
".momo/integration/cursor/",
|
|
24
|
-
".momo/integration/lingma/",
|
|
25
|
-
".momo/integration/qoder/",
|
|
26
|
-
".momo/integration/windsurf/",
|
|
27
|
-
".momo/integration/github/",
|
|
28
|
-
".momo/integration/claude-code/",
|
|
29
|
-
".momo/integration/chatgpt/",
|
|
30
|
-
".momo/integration/auggie/",
|
|
31
|
-
".momo/integration/cline/",
|
|
32
|
-
".momo/integration/roocode/",
|
|
33
|
-
".momo/integration/codebuddy/",
|
|
34
|
-
".momo/integration/costrict/",
|
|
35
|
-
".momo/integration/crush/",
|
|
36
|
-
".momo/integration/factory/",
|
|
37
|
-
".momo/integration/gemini/",
|
|
38
|
-
".momo/integration/opencode/",
|
|
39
|
-
".momo/integration/kilo/",
|
|
40
|
-
".momo/integration/codex/",
|
|
41
|
-
".momo/integration/amazonq/",
|
|
42
|
-
".momo/integration/qwen/",
|
|
43
|
-
".momo/reference/maven",
|
|
44
|
-
".momo/reference/npm",
|
|
45
|
-
".momo/.working/"
|
|
13
|
+
".r2mo/specs/",
|
|
14
|
+
".r2mo/changes/archive/"
|
|
46
15
|
];
|
|
47
16
|
|
|
48
17
|
// 创建所有目录
|
|
49
|
-
const results = [];
|
|
50
18
|
for (const folder of folders) {
|
|
51
19
|
Ec.waiting("创建目录:" + folder);
|
|
52
20
|
const directory = path.resolve(baseDir, folder);
|
|
53
21
|
try {
|
|
54
22
|
await fsAsync.mkdir(directory, {recursive: true});
|
|
55
|
-
|
|
23
|
+
|
|
24
|
+
// 如果目录为空,创建 .placeholder 文件
|
|
25
|
+
const files = await fsAsync.readdir(directory);
|
|
26
|
+
if (files.length === 0) {
|
|
27
|
+
const placeholderPath = path.join(directory, '.placeholder');
|
|
28
|
+
await fsAsync.writeFile(placeholderPath, '');
|
|
29
|
+
Ec.waiting(` 创建占位符文件: ${folder}.placeholder`);
|
|
30
|
+
}
|
|
56
31
|
} catch (error) {
|
|
57
32
|
if (process.platform === 'win32') {
|
|
58
33
|
Ec.waiting(`💡 Windows 用户提示: 创建目录失败,可能是由于权限不足或路径包含非法字符`);
|
|
@@ -60,290 +35,135 @@ const _ioDirectory = async (baseDir) => {
|
|
|
60
35
|
throw error;
|
|
61
36
|
}
|
|
62
37
|
}
|
|
63
|
-
|
|
64
|
-
}
|
|
38
|
+
};
|
|
65
39
|
|
|
40
|
+
/**
|
|
41
|
+
* 创建和拷贝文件
|
|
42
|
+
* @param {string} baseDir 基础目录
|
|
43
|
+
*/
|
|
66
44
|
const _ioFile = async (baseDir) => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
* .momo/advanced/
|
|
72
|
-
* .momo/scripts/
|
|
73
|
-
* .momo/specification/changes/
|
|
74
|
-
* .momo/specification/actor/
|
|
75
|
-
* .momo/specification/.archives/
|
|
76
|
-
* .momo/specification/.activities/
|
|
77
|
-
* .momo/integration/ 下所有类型的子目录
|
|
78
|
-
*/
|
|
79
|
-
|
|
80
|
-
/*
|
|
81
|
-
* 拷贝文件目录
|
|
82
|
-
* _template/specification/project.md
|
|
83
|
-
* _template/specification/project-model.md
|
|
84
|
-
* _template/specification/requirement.md
|
|
85
|
-
*/
|
|
86
|
-
|
|
87
|
-
// 检查是否存在任何已存在的文件
|
|
88
|
-
let hasExistingFiles = false;
|
|
89
|
-
const templateDir = path.resolve(__dirname, "../_template/LAIN");
|
|
90
|
-
|
|
91
|
-
// 检查模板文件
|
|
92
|
-
const templateFiles = [
|
|
93
|
-
{
|
|
94
|
-
source: "specification/project.md",
|
|
95
|
-
target: ".momo/specification/project.md"
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
source: "specification/project-model.md",
|
|
99
|
-
target: ".momo/specification/project-model.md"
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
source: "specification/requirement.md",
|
|
103
|
-
target: ".momo/specification/requirement.md"
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
source: ".gitignore",
|
|
107
|
-
target: ".momo/.gitignore"
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
source: "AGENTS.md",
|
|
111
|
-
target: ".momo/AGENTS.md"
|
|
112
|
-
}
|
|
113
|
-
];
|
|
114
|
-
|
|
115
|
-
for (const file of templateFiles) {
|
|
116
|
-
const sourcePath = path.resolve(templateDir, file.source);
|
|
117
|
-
const targetPath = path.resolve(baseDir, file.target);
|
|
118
|
-
|
|
119
|
-
// 检查源文件是否存在且目标文件已存在
|
|
120
|
-
if (fs.existsSync(sourcePath) && fs.existsSync(targetPath)) {
|
|
121
|
-
hasExistingFiles = true;
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
45
|
+
// 确保 .r2mo 目录存在
|
|
46
|
+
const r2moDir = path.resolve(baseDir, '.r2mo');
|
|
47
|
+
if (!fs.existsSync(r2moDir)) {
|
|
48
|
+
await fsAsync.mkdir(r2moDir, {recursive: true});
|
|
124
49
|
}
|
|
125
50
|
|
|
126
|
-
//
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
if (fs.existsSync(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
let shouldOverwrite = true;
|
|
136
|
-
if (hasExistingFiles) {
|
|
137
|
-
const answer = await Ec.ask("检测到已存在文件,是否全部覆盖?(y/N): ");
|
|
138
|
-
shouldOverwrite = (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
139
|
-
if (!shouldOverwrite) {
|
|
140
|
-
Ec.waiting("跳过所有已存在文件的覆盖操作");
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// 创建空文件的目录列表
|
|
145
|
-
const emptyFileDirs = [
|
|
146
|
-
".momo/prompt/",
|
|
147
|
-
".momo/template/",
|
|
148
|
-
".momo/scripts/",
|
|
149
|
-
".momo/reference/maven",
|
|
150
|
-
".momo/reference/npm",
|
|
151
|
-
".momo/specification/changes/",
|
|
152
|
-
".momo/specification/actor/",
|
|
153
|
-
".momo/specification/.archives/",
|
|
154
|
-
".momo/specification/.activities/",
|
|
155
|
-
".momo/source/",
|
|
156
|
-
".momo/integration/openspec/",
|
|
157
|
-
".momo/integration/spec-kit/",
|
|
158
|
-
".momo/integration/kiro/",
|
|
159
|
-
".momo/integration/trea/",
|
|
160
|
-
".momo/integration/cursor/",
|
|
161
|
-
".momo/integration/lingma/",
|
|
162
|
-
".momo/integration/qoder/",
|
|
163
|
-
".momo/integration/windsurf/",
|
|
164
|
-
".momo/integration/github/",
|
|
165
|
-
".momo/integration/claude-code/",
|
|
166
|
-
".momo/integration/chatgpt/",
|
|
167
|
-
// 新增的 integration 目录(使用小写)
|
|
168
|
-
".momo/integration/auggie/",
|
|
169
|
-
".momo/integration/cline/",
|
|
170
|
-
".momo/integration/roocode/",
|
|
171
|
-
".momo/integration/codebuddy/",
|
|
172
|
-
".momo/integration/costrict/",
|
|
173
|
-
".momo/integration/crush/",
|
|
174
|
-
".momo/integration/factory/",
|
|
175
|
-
".momo/integration/gemini/",
|
|
176
|
-
".momo/integration/opencode/",
|
|
177
|
-
".momo/integration/kilo/",
|
|
178
|
-
".momo/integration/codex/",
|
|
179
|
-
".momo/integration/amazonq/",
|
|
180
|
-
".momo/integration/qwen/"
|
|
181
|
-
];
|
|
182
|
-
|
|
183
|
-
// 为每个目录创建一个空的 .gitkeep 文件,确保目录被git跟踪
|
|
184
|
-
for (const dir of emptyFileDirs) {
|
|
185
|
-
const fullPath = path.resolve(baseDir, dir, ".gitkeep");
|
|
186
|
-
// 确保目录存在后再创建文件
|
|
187
|
-
const dirPath = path.dirname(fullPath);
|
|
188
|
-
if (!fs.existsSync(dirPath)) {
|
|
189
|
-
await fsAsync.mkdir(dirPath, {recursive: true});
|
|
51
|
+
// 1. 拷贝 AGENTS.md
|
|
52
|
+
const agentsSourcePath = path.resolve(__dirname, '../_template/LAIN/AGENTS.md');
|
|
53
|
+
const agentsTargetPath = path.resolve(baseDir, '.r2mo/AGENTS.md');
|
|
54
|
+
|
|
55
|
+
if (fs.existsSync(agentsSourcePath)) {
|
|
56
|
+
// 确保目标目录存在
|
|
57
|
+
const targetDir = path.dirname(agentsTargetPath);
|
|
58
|
+
if (!fs.existsSync(targetDir)) {
|
|
59
|
+
await fsAsync.mkdir(targetDir, {recursive: true});
|
|
190
60
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
await
|
|
61
|
+
|
|
62
|
+
// 检查目标文件是否已存在
|
|
63
|
+
if (fs.existsSync(agentsTargetPath)) {
|
|
64
|
+
const answer = await Ec.ask("检测到 .r2mo/AGENTS.md 已存在,是否覆盖?(y/N): ");
|
|
65
|
+
if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
66
|
+
Ec.waiting("拷贝模板文件:.r2mo/AGENTS.md");
|
|
67
|
+
await fsAsync.copyFile(agentsSourcePath, agentsTargetPath);
|
|
68
|
+
} else {
|
|
69
|
+
Ec.waiting("跳过文件:.r2mo/AGENTS.md");
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
Ec.waiting("拷贝模板文件:.r2mo/AGENTS.md");
|
|
73
|
+
await fsAsync.copyFile(agentsSourcePath, agentsTargetPath);
|
|
195
74
|
}
|
|
75
|
+
} else {
|
|
76
|
+
Ec.warn(`⚠ 模板文件不存在: ${agentsSourcePath}`);
|
|
196
77
|
}
|
|
197
78
|
|
|
198
|
-
//
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
".
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
".
|
|
205
|
-
|
|
206
|
-
".momo/integration/qoder/",
|
|
207
|
-
".momo/integration/windsurf/",
|
|
208
|
-
".momo/integration/github/",
|
|
209
|
-
".momo/integration/claude-code/",
|
|
210
|
-
".momo/integration/chatgpt/",
|
|
211
|
-
// 新增的 integration 目录(使用小写)
|
|
212
|
-
".momo/integration/auggie/",
|
|
213
|
-
".momo/integration/cline/",
|
|
214
|
-
".momo/integration/roocode/",
|
|
215
|
-
".momo/integration/codebuddy/",
|
|
216
|
-
".momo/integration/costrict/",
|
|
217
|
-
".momo/integration/crush/",
|
|
218
|
-
".momo/integration/factory/",
|
|
219
|
-
".momo/integration/gemini/",
|
|
220
|
-
".momo/integration/opencode/",
|
|
221
|
-
".momo/integration/kilo/",
|
|
222
|
-
".momo/integration/codex/",
|
|
223
|
-
".momo/integration/amazonq/",
|
|
224
|
-
".momo/integration/qwen/"
|
|
225
|
-
];
|
|
226
|
-
|
|
227
|
-
// 通用的 LLM 配置模板
|
|
228
|
-
const configTemplate = {
|
|
229
|
-
llm: "", // LLM 类型标识符
|
|
230
|
-
token: "", // API 访问令牌
|
|
231
|
-
baseUrl: "", // API 基础 URL(可选)
|
|
232
|
-
model: "", // 模型名称(可选)
|
|
233
|
-
temperature: 0.7, // 温度参数(可选)
|
|
234
|
-
maxTokens: 2048, // 最大令牌数(可选)
|
|
235
|
-
topP: 1.0, // Top-P 参数(可选)
|
|
236
|
-
frequencyPenalty: 0, // 频率惩罚(可选)
|
|
237
|
-
presencePenalty: 0 // 存在惩罚(可选)
|
|
238
|
-
};
|
|
79
|
+
// 2. 创建 project.md(留空)
|
|
80
|
+
const projectTargetPath = path.resolve(baseDir, '.r2mo/project.md');
|
|
81
|
+
if (!fs.existsSync(projectTargetPath)) {
|
|
82
|
+
Ec.waiting("创建文件:.r2mo/project.md");
|
|
83
|
+
await fsAsync.writeFile(projectTargetPath, '');
|
|
84
|
+
} else {
|
|
85
|
+
Ec.waiting("文件已存在:.r2mo/project.md");
|
|
86
|
+
}
|
|
239
87
|
|
|
240
|
-
//
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
88
|
+
// 3. 拷贝 .gitignore
|
|
89
|
+
const gitignoreSourcePath = path.resolve(__dirname, '../_template/LAIN/.gitignore');
|
|
90
|
+
const gitignoreTargetPath = path.resolve(baseDir, '.r2mo/.gitignore');
|
|
91
|
+
|
|
92
|
+
if (fs.existsSync(gitignoreSourcePath)) {
|
|
93
|
+
// 确保目标目录存在
|
|
94
|
+
const targetDir = path.dirname(gitignoreTargetPath);
|
|
95
|
+
if (!fs.existsSync(targetDir)) {
|
|
96
|
+
await fsAsync.mkdir(targetDir, {recursive: true});
|
|
247
97
|
}
|
|
248
|
-
|
|
249
|
-
//
|
|
250
|
-
if (
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
98
|
+
|
|
99
|
+
// 检查目标文件是否已存在
|
|
100
|
+
if (fs.existsSync(gitignoreTargetPath)) {
|
|
101
|
+
const answer = await Ec.ask("检测到 .r2mo/.gitignore 已存在,是否覆盖?(y/N): ");
|
|
102
|
+
if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
103
|
+
Ec.waiting("拷贝模板文件:.r2mo/.gitignore");
|
|
104
|
+
await fsAsync.copyFile(gitignoreSourcePath, gitignoreTargetPath);
|
|
105
|
+
} else {
|
|
106
|
+
Ec.waiting("跳过文件:.r2mo/.gitignore");
|
|
107
|
+
}
|
|
257
108
|
} else {
|
|
258
|
-
Ec.waiting("
|
|
109
|
+
Ec.waiting("拷贝模板文件:.r2mo/.gitignore");
|
|
110
|
+
await fsAsync.copyFile(gitignoreSourcePath, gitignoreTargetPath);
|
|
259
111
|
}
|
|
112
|
+
} else {
|
|
113
|
+
Ec.warn(`⚠ 模板文件不存在: ${gitignoreSourcePath}`);
|
|
260
114
|
}
|
|
261
115
|
|
|
262
|
-
//
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (
|
|
269
|
-
//
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
if (file.target.startsWith(".momo/specification/")) {
|
|
279
|
-
Ec.waiting("跳过文件:" + file.target + " ".yellow + "(.momo/specification目录下的文件可能已被修改)".yellow);
|
|
280
|
-
} else if (shouldOverwrite) {
|
|
281
|
-
// 目标文件已存在,根据用户选择决定是否覆盖(非specification目录下的文件)
|
|
282
|
-
Ec.waiting("覆盖模板文件:" + file.target);
|
|
283
|
-
await fsAsync.copyFile(sourcePath, targetPath);
|
|
116
|
+
// 4. 拷贝 .obsidian 目录(递归拷贝整个目录)
|
|
117
|
+
const obsidianSourcePath = path.resolve(__dirname, '../_template/LAIN/.obsidian');
|
|
118
|
+
const obsidianTargetPath = path.resolve(baseDir, '.r2mo/.obsidian');
|
|
119
|
+
|
|
120
|
+
if (fs.existsSync(obsidianSourcePath)) {
|
|
121
|
+
const stat = await fsAsync.stat(obsidianSourcePath);
|
|
122
|
+
if (stat.isDirectory()) {
|
|
123
|
+
// 检查目标目录是否已存在
|
|
124
|
+
if (fs.existsSync(obsidianTargetPath)) {
|
|
125
|
+
const answer = await Ec.ask("检测到 .r2mo/.obsidian 目录已存在,是否覆盖?(y/N): ");
|
|
126
|
+
if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
127
|
+
Ec.waiting("拷贝模板目录:.r2mo/.obsidian");
|
|
128
|
+
// 删除现有目录
|
|
129
|
+
await fsAsync.rm(obsidianTargetPath, { recursive: true, force: true });
|
|
130
|
+
// 拷贝新目录
|
|
131
|
+
await copyDir(obsidianSourcePath, obsidianTargetPath);
|
|
284
132
|
} else {
|
|
285
|
-
Ec.waiting("
|
|
133
|
+
Ec.waiting("跳过目录:.r2mo/.obsidian");
|
|
286
134
|
}
|
|
287
135
|
} else {
|
|
288
|
-
Ec.waiting("
|
|
289
|
-
await
|
|
136
|
+
Ec.waiting("拷贝模板目录:.r2mo/.obsidian");
|
|
137
|
+
await copyDir(obsidianSourcePath, obsidianTargetPath);
|
|
290
138
|
}
|
|
291
139
|
} else {
|
|
292
|
-
|
|
293
|
-
if (!fs.existsSync(targetPath)) {
|
|
294
|
-
Ec.waiting("创建空文件:" + targetPath);
|
|
295
|
-
// 确保目标目录存在
|
|
296
|
-
const targetDir = path.dirname(targetPath);
|
|
297
|
-
if (!fs.existsSync(targetDir)) {
|
|
298
|
-
await fsAsync.mkdir(targetDir, {recursive: true});
|
|
299
|
-
}
|
|
300
|
-
await fsAsync.writeFile(targetPath, "");
|
|
301
|
-
}
|
|
140
|
+
Ec.warn(`⚠ .obsidian 不是目录: ${obsidianSourcePath}`);
|
|
302
141
|
}
|
|
142
|
+
} else {
|
|
143
|
+
Ec.warn(`⚠ 模板目录不存在: ${obsidianSourcePath}`);
|
|
303
144
|
}
|
|
304
145
|
|
|
305
|
-
//
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
await fsAsync.mkdir(actorDir, {recursive: true});
|
|
311
|
-
}
|
|
146
|
+
// 5. 确保空目录有 .placeholder 文件
|
|
147
|
+
const emptyDirs = [
|
|
148
|
+
'.r2mo/specs/',
|
|
149
|
+
'.r2mo/changes/archive/'
|
|
150
|
+
];
|
|
312
151
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
Ec.waiting("拷贝模板文件:" + ".momo/advanced/actor.md");
|
|
324
|
-
await fsAsync.copyFile(actorSourcePath, actorTargetPath);
|
|
325
|
-
}
|
|
326
|
-
} else {
|
|
327
|
-
// 如果模板文件不存在,则创建空文件
|
|
328
|
-
if (!fs.existsSync(actorTargetPath)) {
|
|
329
|
-
Ec.waiting("创建文件:" + actorTargetPath);
|
|
330
|
-
// 确保目标目录存在
|
|
331
|
-
const actorDir = path.dirname(actorTargetPath);
|
|
332
|
-
if (!fs.existsSync(actorDir)) {
|
|
333
|
-
await fsAsync.mkdir(actorDir, {recursive: true});
|
|
152
|
+
for (const dir of emptyDirs) {
|
|
153
|
+
const dirPath = path.resolve(baseDir, dir);
|
|
154
|
+
if (fs.existsSync(dirPath)) {
|
|
155
|
+
const files = await fsAsync.readdir(dirPath);
|
|
156
|
+
if (files.length === 0 || (files.length === 1 && files[0] === '.placeholder')) {
|
|
157
|
+
const placeholderPath = path.join(dirPath, '.placeholder');
|
|
158
|
+
if (!fs.existsSync(placeholderPath)) {
|
|
159
|
+
await fsAsync.writeFile(placeholderPath, '');
|
|
160
|
+
Ec.waiting(` 创建占位符文件: ${dir}.placeholder`);
|
|
161
|
+
}
|
|
334
162
|
}
|
|
335
|
-
|
|
336
|
-
await fsAsync.writeFile(actorTargetPath, "");
|
|
337
163
|
}
|
|
338
164
|
}
|
|
339
165
|
|
|
340
|
-
//
|
|
341
|
-
const momoAdvancedDir = path.resolve(baseDir, ".momo/advanced/");
|
|
342
|
-
if (!fs.existsSync(momoAdvancedDir)) {
|
|
343
|
-
await fsAsync.mkdir(momoAdvancedDir, {recursive: true});
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// 处理 .gitignore 文件,确保 .momo 目录被忽略
|
|
166
|
+
// 6. 处理项目根目录的 .gitignore 文件,确保 .r2mo 目录被忽略
|
|
347
167
|
const gitignorePath = path.resolve(baseDir, ".gitignore");
|
|
348
168
|
let gitignoreContent = "";
|
|
349
169
|
|
|
@@ -353,100 +173,47 @@ const _ioFile = async (baseDir) => {
|
|
|
353
173
|
gitignoreContent = await fsAsync.readFile(gitignorePath, 'utf8');
|
|
354
174
|
}
|
|
355
175
|
|
|
356
|
-
// 检查 .
|
|
357
|
-
if (!gitignoreContent.includes('.
|
|
176
|
+
// 检查 .r2mo 是否已经在 .gitignore 中
|
|
177
|
+
if (!gitignoreContent.includes('.r2mo')) {
|
|
358
178
|
// 如果内容不为空,先添加一个换行
|
|
359
179
|
if (gitignoreContent && !gitignoreContent.endsWith('\n')) {
|
|
360
180
|
gitignoreContent += '\n';
|
|
361
181
|
}
|
|
362
|
-
// 添加 .
|
|
363
|
-
gitignoreContent += '.
|
|
182
|
+
// 添加 .r2mo 到 .gitignore
|
|
183
|
+
gitignoreContent += '.r2mo\n';
|
|
364
184
|
|
|
365
185
|
// 写入 .gitignore 文件
|
|
366
186
|
await fsAsync.writeFile(gitignorePath, gitignoreContent);
|
|
367
|
-
Ec.waiting("已更新 .gitignore 文件,添加 .
|
|
368
|
-
} else {
|
|
369
|
-
Ec.waiting(".gitignore 文件已包含 .momo 目录,无需更新");
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// 创建 .claude/skills/ 目录并添加占位符文件
|
|
373
|
-
const claudeSkillsDir = path.resolve(baseDir, ".claude", "skills");
|
|
374
|
-
await fsAsync.mkdir(claudeSkillsDir, {recursive: true});
|
|
375
|
-
Ec.waiting("创建 .claude/skills/ 目录");
|
|
376
|
-
|
|
377
|
-
// 在 .claude/skills/ 目录中创建 .placeholder 文件
|
|
378
|
-
const placeholderPath = path.join(claudeSkillsDir, ".placeholder");
|
|
379
|
-
if (!fs.existsSync(placeholderPath)) {
|
|
380
|
-
await fsAsync.writeFile(placeholderPath, "");
|
|
381
|
-
Ec.waiting("在 .claude/skills/ 目录中创建 .placeholder 文件");
|
|
187
|
+
Ec.waiting("已更新 .gitignore 文件,添加 .r2mo 目录到忽略列表");
|
|
382
188
|
} else {
|
|
383
|
-
Ec.waiting(".
|
|
189
|
+
Ec.waiting(".gitignore 文件已包含 .r2mo 目录,无需更新");
|
|
384
190
|
}
|
|
385
|
-
}
|
|
191
|
+
};
|
|
386
192
|
|
|
387
193
|
/**
|
|
388
194
|
* 目录结构说明
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
* /
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
* /
|
|
404
|
-
* /
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
408
|
-
*
|
|
409
|
-
*
|
|
410
|
-
*
|
|
411
|
-
* /M01-T001-02.md ......
|
|
412
|
-
* /design.md ( OpenSpec ) 变更设计说明文件
|
|
413
|
-
* /change.md ( OpenSpec -> specs/profile/spec.md ) 变更说明文件
|
|
414
|
-
* /actor/ 角色目录 / 定义了 Agent 所具有的角色信息,可自定义角色
|
|
415
|
-
* /{name}/stack.md 角色技能(技术栈)说明文件
|
|
416
|
-
* /{name}/limit.md 角色限制说明文件
|
|
417
|
-
* /.archives/ 归档记录
|
|
418
|
-
* /.activities/
|
|
419
|
-
* /actor@plan-XXX/ 角色在某个计划下的历史执行记录(带时间戳)
|
|
420
|
-
* /source/ 源代码目录
|
|
421
|
-
* /develop-01
|
|
422
|
-
* /develop-02
|
|
423
|
-
* /develop-03
|
|
424
|
-
* /integration/ 集成目录(特殊集成配置)
|
|
425
|
-
* openspec/ OpenSpec 目录
|
|
426
|
-
* /config.json 配置文件
|
|
427
|
-
* spec-kit/ SpecKit 目录
|
|
428
|
-
* kiro/ Kiro 目录
|
|
429
|
-
* trea/ Trea 目录
|
|
430
|
-
* cursor/ Cursor 目录
|
|
431
|
-
* lingma/ Lingma 目录
|
|
432
|
-
* qoder/ Qoder 目录
|
|
433
|
-
* windsurf/ WindSurf 目录
|
|
434
|
-
* github/ Github Copilot 目录
|
|
435
|
-
* claude-code/ Claude Code 目录
|
|
436
|
-
* chatgpt/ ChatGPT 目录
|
|
437
|
-
* auggie/ Auggie (Augment CLI) 目录
|
|
438
|
-
* cline/ Cline 目录
|
|
439
|
-
* roocode/ RooCode 目录
|
|
440
|
-
* codebuddy/ CodeBuddy Code (CLI) 目录
|
|
441
|
-
* costrict/ CoStrict 目录
|
|
442
|
-
* crush/ Crush 目录
|
|
443
|
-
* factory/ Factory Droid 目录
|
|
444
|
-
* gemini/ Gemini CLI 目录
|
|
445
|
-
* opencode/ OpenCode 目录
|
|
446
|
-
* kilo/ Kilo Code 目录
|
|
447
|
-
* codex/ Codex 目录
|
|
448
|
-
* amazonq/ Amazon Q Developer 目录
|
|
449
|
-
* qwen/ Qwen Code 目录
|
|
195
|
+
*
|
|
196
|
+
* 主体遵循 OpenSpec 目录结构:
|
|
197
|
+
*
|
|
198
|
+
* .r2mo/ 根目录
|
|
199
|
+
* /specs/ 规格说明目录
|
|
200
|
+
* /changes/ 变更详情
|
|
201
|
+
* /archive/ 归档记录
|
|
202
|
+
* /AGENTS.md AI Agents 集成说明(从模板拷贝)
|
|
203
|
+
* /project.md 项目基本说明文件(留空,待填写)
|
|
204
|
+
*
|
|
205
|
+
* OpenSpec 变更结构(在 .r2mo/changes/ 下创建):
|
|
206
|
+
* /changes/
|
|
207
|
+
* /change-XXX/
|
|
208
|
+
* /implementation.md 实现方案步骤说明
|
|
209
|
+
* /proposal.md ( OpenSpec ) 变更提案说明文件
|
|
210
|
+
* /tasks.md ( OpenSpec ) 变更任务列表说明 -> 链接到 tasks/ 目录下的具体任务清单中
|
|
211
|
+
* /tasks 计划详细内容,如果拆分成 Task 则对应到 Tasks/plan-XXX/ 目录中
|
|
212
|
+
* /M01-T001-01.md 具体任务文件,M代表模块,T代表任务,001代表任务编号,01代表子任务序号
|
|
213
|
+
* /M01-T001-02.md ......
|
|
214
|
+
* /design.md ( OpenSpec ) 变更设计说明文件
|
|
215
|
+
* /change.md ( OpenSpec -> specs/profile/spec.md ) 变更说明文件
|
|
216
|
+
*
|
|
450
217
|
* @param options
|
|
451
218
|
*/
|
|
452
219
|
module.exports = (options) => {
|
|
@@ -455,17 +222,26 @@ module.exports = (options) => {
|
|
|
455
222
|
/*
|
|
456
223
|
* 基本信息
|
|
457
224
|
*/
|
|
458
|
-
const directory = parsed.
|
|
225
|
+
const directory = parsed.dir || parsed.d || '.';
|
|
459
226
|
// 创建基础目录结构
|
|
460
227
|
const basePath = path.resolve(process.cwd(), directory);
|
|
461
228
|
|
|
462
|
-
Ec.waiting(`准备在目录 "${basePath}" 中初始化
|
|
229
|
+
Ec.waiting(`准备在目录 "${basePath}" 中初始化 R2MO 工程...`);
|
|
463
230
|
|
|
464
231
|
// 创建目录结构
|
|
465
232
|
_ioDirectory(basePath)
|
|
466
233
|
.then(() => _ioFile(basePath))
|
|
467
234
|
.then(() => {
|
|
468
|
-
Ec.info('✅
|
|
235
|
+
Ec.info('✅ R2MO / 项目初始化完成!');
|
|
236
|
+
console.log('');
|
|
237
|
+
Ec.info('已创建以下目录和文件:');
|
|
238
|
+
console.log(' .r2mo/specs/');
|
|
239
|
+
console.log(' .r2mo/changes/archive/');
|
|
240
|
+
console.log(' .r2mo/AGENTS.md');
|
|
241
|
+
console.log(' .r2mo/project.md');
|
|
242
|
+
console.log(' .r2mo/.gitignore');
|
|
243
|
+
console.log(' .r2mo/.obsidian/');
|
|
244
|
+
console.log('');
|
|
469
245
|
// 关闭 readline 接口
|
|
470
246
|
Ec.askClose();
|
|
471
247
|
// 退出程序
|
|
@@ -478,4 +254,4 @@ module.exports = (options) => {
|
|
|
478
254
|
// 退出程序
|
|
479
255
|
process.exit(1);
|
|
480
256
|
});
|
|
481
|
-
}
|
|
257
|
+
};
|