momo-ai 1.0.20 → 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/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/SKILL.md +101 -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/.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 +12 -148
- package/docs/images/logo.jpeg +0 -0
- package/docs/images/r2mo-lain.png +0 -0
- package/install.sh +1 -0
- package/package.json +15 -11
- package/skills/r2mo-rad-domain/SKILL.md +70 -0
- package/skills/r2mo-rad-lain/SKILL.md +101 -0
- package/src/_mcp/skills-server.mjs +70 -0
- package/src/_skill/repositories.json +22 -0
- 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/help.json +5 -0
- package/src/commander/init.json +1 -1
- package/src/commander/mcp.json +13 -0
- package/src/commander/mmr0.json +6 -0
- package/src/commander/mmr2.json +6 -0
- package/src/commander/open.json +8 -2
- package/src/executor/executeApp.js +133 -0
- package/src/executor/executeApply.js +611 -0
- package/src/executor/executeAsk.js +274 -0
- package/src/executor/executeDocs.js +498 -0
- package/src/executor/executeDomain.js +293 -0
- package/src/executor/executeEnv.js +48 -38
- package/src/executor/executeHelp.js +77 -16
- package/src/executor/executeInit.js +176 -346
- package/src/executor/executeMcp.js +363 -0
- package/src/executor/executeMmr0.js +488 -0
- package/src/executor/executeMmr2.js +880 -0
- package/src/executor/executeOpen.js +144 -125
- package/src/executor/index.js +17 -39
- package/src/momo.js +2 -1
- 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-args.js +39 -0
- package/src/utils/momo-file-utils.js +75 -0
- package/src/utils/momo-menu.js +84 -0
- 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/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
|
@@ -2,42 +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
|
-
"specification/",
|
|
13
|
-
"specification/changes/",
|
|
14
|
-
"specification/actor/",
|
|
15
|
-
"specification/.archives/",
|
|
16
|
-
"specification/.activities/",
|
|
17
|
-
"source/",
|
|
18
|
-
"integration/",
|
|
19
|
-
"integration/openspec/",
|
|
20
|
-
"integration/spec-kit/",
|
|
21
|
-
"integration/kiro/",
|
|
22
|
-
"integration/trea/",
|
|
23
|
-
"integration/cursor/",
|
|
24
|
-
"integration/lingma/",
|
|
25
|
-
"integration/qoder/",
|
|
26
|
-
"integration/windsurf/",
|
|
27
|
-
"integration/github/",
|
|
28
|
-
"integration/claude-code/",
|
|
29
|
-
"integration/chatgpt/",
|
|
30
|
-
".working/"
|
|
13
|
+
".r2mo/specs/",
|
|
14
|
+
".r2mo/changes/archive/"
|
|
31
15
|
];
|
|
32
16
|
|
|
33
17
|
// 创建所有目录
|
|
34
|
-
const results = [];
|
|
35
18
|
for (const folder of folders) {
|
|
36
19
|
Ec.waiting("创建目录:" + folder);
|
|
37
20
|
const directory = path.resolve(baseDir, folder);
|
|
38
21
|
try {
|
|
39
22
|
await fsAsync.mkdir(directory, {recursive: true});
|
|
40
|
-
|
|
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
|
+
}
|
|
41
31
|
} catch (error) {
|
|
42
32
|
if (process.platform === 'win32') {
|
|
43
33
|
Ec.waiting(`💡 Windows 用户提示: 创建目录失败,可能是由于权限不足或路径包含非法字符`);
|
|
@@ -45,354 +35,185 @@ const _ioDirectory = async (baseDir) => {
|
|
|
45
35
|
throw error;
|
|
46
36
|
}
|
|
47
37
|
}
|
|
48
|
-
|
|
49
|
-
}
|
|
38
|
+
};
|
|
50
39
|
|
|
40
|
+
/**
|
|
41
|
+
* 创建和拷贝文件
|
|
42
|
+
* @param {string} baseDir 基础目录
|
|
43
|
+
*/
|
|
51
44
|
const _ioFile = async (baseDir) => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
* .momo/advanced/
|
|
57
|
-
* .momo/scripts/
|
|
58
|
-
*
|
|
59
|
-
* specification/changes/
|
|
60
|
-
* specification/actor/
|
|
61
|
-
* specification/.archives/
|
|
62
|
-
* specification/.activities/
|
|
63
|
-
*
|
|
64
|
-
* integration/ 下所有类型的子目录
|
|
65
|
-
*/
|
|
66
|
-
|
|
67
|
-
/*
|
|
68
|
-
* 拷贝文件目录
|
|
69
|
-
* _template/specification/project.md
|
|
70
|
-
* _template/specification/project-model.md
|
|
71
|
-
* _template/specification/requirement.md
|
|
72
|
-
*/
|
|
73
|
-
|
|
74
|
-
// 检查是否存在任何已存在的文件
|
|
75
|
-
let hasExistingFiles = false;
|
|
76
|
-
const templateDir = path.resolve(__dirname, "../_template/LAIN");
|
|
77
|
-
|
|
78
|
-
// 检查模板文件
|
|
79
|
-
const templateFiles = [
|
|
80
|
-
{
|
|
81
|
-
source: "specification/project.md",
|
|
82
|
-
target: "specification/project.md"
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
source: "specification/project-model.md",
|
|
86
|
-
target: "specification/project-model.md"
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
source: "specification/requirement.md",
|
|
90
|
-
target: "specification/requirement.md"
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
source: ".gitignore",
|
|
94
|
-
target: ".gitignore"
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
source: "AGENTS.md",
|
|
98
|
-
target: "AGENTS.md"
|
|
99
|
-
}
|
|
100
|
-
];
|
|
101
|
-
|
|
102
|
-
for (const file of templateFiles) {
|
|
103
|
-
const sourcePath = path.resolve(templateDir, file.source);
|
|
104
|
-
const targetPath = path.resolve(baseDir, file.target);
|
|
105
|
-
|
|
106
|
-
// 检查源文件是否存在且目标文件已存在
|
|
107
|
-
if (fs.existsSync(sourcePath) && fs.existsSync(targetPath)) {
|
|
108
|
-
hasExistingFiles = true;
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// 检查 actor.md 文件
|
|
114
|
-
const actorSourcePath = path.resolve(templateDir, ".momo/advanced/actor.md");
|
|
115
|
-
const actorTargetPath = path.resolve(baseDir, ".momo/advanced/actor.md");
|
|
116
|
-
|
|
117
|
-
if (fs.existsSync(actorSourcePath) && fs.existsSync(actorTargetPath)) {
|
|
118
|
-
hasExistingFiles = true;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// 如果有已存在的文件,询问用户是否覆盖
|
|
122
|
-
let shouldOverwrite = true;
|
|
123
|
-
if (hasExistingFiles) {
|
|
124
|
-
const answer = await Ec.ask("检测到已存在文件,是否全部覆盖?(y/N): ");
|
|
125
|
-
shouldOverwrite = (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
126
|
-
if (!shouldOverwrite) {
|
|
127
|
-
Ec.waiting("跳过所有已存在文件的覆盖操作");
|
|
128
|
-
}
|
|
45
|
+
// 确保 .r2mo 目录存在
|
|
46
|
+
const r2moDir = path.resolve(baseDir, '.r2mo');
|
|
47
|
+
if (!fs.existsSync(r2moDir)) {
|
|
48
|
+
await fsAsync.mkdir(r2moDir, {recursive: true});
|
|
129
49
|
}
|
|
130
50
|
|
|
131
|
-
//
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
"specification/.archives/",
|
|
141
|
-
"specification/.activities/",
|
|
142
|
-
"source/",
|
|
143
|
-
"integration/openspec/",
|
|
144
|
-
"integration/spec-kit/",
|
|
145
|
-
"integration/kiro/",
|
|
146
|
-
"integration/trea/",
|
|
147
|
-
"integration/cursor/",
|
|
148
|
-
"integration/lingma/",
|
|
149
|
-
"integration/qoder/",
|
|
150
|
-
"integration/windsurf/",
|
|
151
|
-
"integration/github/",
|
|
152
|
-
"integration/claude-code/",
|
|
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/"
|
|
168
|
-
];
|
|
169
|
-
|
|
170
|
-
// 为每个目录创建一个空的 .gitkeep 文件,确保目录被git跟踪
|
|
171
|
-
for (const dir of emptyFileDirs) {
|
|
172
|
-
const fullPath = path.resolve(baseDir, dir, ".gitkeep");
|
|
173
|
-
// 确保目录存在后再创建文件
|
|
174
|
-
const dirPath = path.dirname(fullPath);
|
|
175
|
-
if (!fs.existsSync(dirPath)) {
|
|
176
|
-
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});
|
|
177
60
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
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);
|
|
182
74
|
}
|
|
75
|
+
} else {
|
|
76
|
+
Ec.warn(`⚠ 模板文件不存在: ${agentsSourcePath}`);
|
|
183
77
|
}
|
|
184
78
|
|
|
185
|
-
//
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
"
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
"
|
|
192
|
-
|
|
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
|
-
};
|
|
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
|
+
}
|
|
226
87
|
|
|
227
|
-
//
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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});
|
|
234
97
|
}
|
|
235
|
-
|
|
236
|
-
//
|
|
237
|
-
if (
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
+
}
|
|
244
108
|
} else {
|
|
245
|
-
Ec.waiting("
|
|
109
|
+
Ec.waiting("拷贝模板文件:.r2mo/.gitignore");
|
|
110
|
+
await fsAsync.copyFile(gitignoreSourcePath, gitignoreTargetPath);
|
|
246
111
|
}
|
|
112
|
+
} else {
|
|
113
|
+
Ec.warn(`⚠ 模板文件不存在: ${gitignoreSourcePath}`);
|
|
247
114
|
}
|
|
248
115
|
|
|
249
|
-
//
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
if (
|
|
256
|
-
//
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (file.target.startsWith("specification/")) {
|
|
266
|
-
Ec.waiting("跳过文件:" + file.target + " ".yellow + "(specification目录下的文件可能已被修改)".yellow);
|
|
267
|
-
} else if (shouldOverwrite) {
|
|
268
|
-
// 目标文件已存在,根据用户选择决定是否覆盖(非specification目录下的文件)
|
|
269
|
-
Ec.waiting("覆盖模板文件:" + file.target);
|
|
270
|
-
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);
|
|
271
132
|
} else {
|
|
272
|
-
Ec.waiting("
|
|
133
|
+
Ec.waiting("跳过目录:.r2mo/.obsidian");
|
|
273
134
|
}
|
|
274
135
|
} else {
|
|
275
|
-
Ec.waiting("
|
|
276
|
-
await
|
|
136
|
+
Ec.waiting("拷贝模板目录:.r2mo/.obsidian");
|
|
137
|
+
await copyDir(obsidianSourcePath, obsidianTargetPath);
|
|
277
138
|
}
|
|
278
139
|
} else {
|
|
279
|
-
|
|
280
|
-
if (!fs.existsSync(targetPath)) {
|
|
281
|
-
Ec.waiting("创建空文件:" + targetPath);
|
|
282
|
-
// 确保目标目录存在
|
|
283
|
-
const targetDir = path.dirname(targetPath);
|
|
284
|
-
if (!fs.existsSync(targetDir)) {
|
|
285
|
-
await fsAsync.mkdir(targetDir, {recursive: true});
|
|
286
|
-
}
|
|
287
|
-
await fsAsync.writeFile(targetPath, "");
|
|
288
|
-
}
|
|
140
|
+
Ec.warn(`⚠ .obsidian 不是目录: ${obsidianSourcePath}`);
|
|
289
141
|
}
|
|
142
|
+
} else {
|
|
143
|
+
Ec.warn(`⚠ 模板目录不存在: ${obsidianSourcePath}`);
|
|
290
144
|
}
|
|
291
145
|
|
|
292
|
-
//
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
await fsAsync.mkdir(actorDir, {recursive: true});
|
|
298
|
-
}
|
|
146
|
+
// 5. 确保空目录有 .placeholder 文件
|
|
147
|
+
const emptyDirs = [
|
|
148
|
+
'.r2mo/specs/',
|
|
149
|
+
'.r2mo/changes/archive/'
|
|
150
|
+
];
|
|
299
151
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
Ec.waiting("拷贝模板文件:" + ".momo/advanced/actor.md");
|
|
311
|
-
await fsAsync.copyFile(actorSourcePath, actorTargetPath);
|
|
312
|
-
}
|
|
313
|
-
} else {
|
|
314
|
-
// 如果模板文件不存在,则创建空文件
|
|
315
|
-
if (!fs.existsSync(actorTargetPath)) {
|
|
316
|
-
Ec.waiting("创建文件:" + actorTargetPath);
|
|
317
|
-
// 确保目标目录存在
|
|
318
|
-
const actorDir = path.dirname(actorTargetPath);
|
|
319
|
-
if (!fs.existsSync(actorDir)) {
|
|
320
|
-
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
|
+
}
|
|
321
162
|
}
|
|
322
|
-
|
|
323
|
-
await fsAsync.writeFile(actorTargetPath, "");
|
|
324
163
|
}
|
|
325
164
|
}
|
|
326
165
|
|
|
327
|
-
//
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
166
|
+
// 6. 处理项目根目录的 .gitignore 文件,确保 .r2mo 目录被忽略
|
|
167
|
+
const gitignorePath = path.resolve(baseDir, ".gitignore");
|
|
168
|
+
let gitignoreContent = "";
|
|
169
|
+
|
|
170
|
+
// 检查 .gitignore 文件是否存在
|
|
171
|
+
if (fs.existsSync(gitignorePath)) {
|
|
172
|
+
// 读取现有的 .gitignore 内容
|
|
173
|
+
gitignoreContent = await fsAsync.readFile(gitignorePath, 'utf8');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 检查 .r2mo 是否已经在 .gitignore 中
|
|
177
|
+
if (!gitignoreContent.includes('.r2mo')) {
|
|
178
|
+
// 如果内容不为空,先添加一个换行
|
|
179
|
+
if (gitignoreContent && !gitignoreContent.endsWith('\n')) {
|
|
180
|
+
gitignoreContent += '\n';
|
|
181
|
+
}
|
|
182
|
+
// 添加 .r2mo 到 .gitignore
|
|
183
|
+
gitignoreContent += '.r2mo\n';
|
|
184
|
+
|
|
185
|
+
// 写入 .gitignore 文件
|
|
186
|
+
await fsAsync.writeFile(gitignorePath, gitignoreContent);
|
|
187
|
+
Ec.waiting("已更新 .gitignore 文件,添加 .r2mo 目录到忽略列表");
|
|
188
|
+
} else {
|
|
189
|
+
Ec.waiting(".gitignore 文件已包含 .r2mo 目录,无需更新");
|
|
331
190
|
}
|
|
332
|
-
}
|
|
191
|
+
};
|
|
333
192
|
|
|
334
193
|
/**
|
|
335
194
|
* 目录结构说明
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
* /
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
* /design.md ( OpenSpec ) 变更设计说明文件
|
|
359
|
-
* /change.md ( OpenSpec -> specs/profile/spec.md ) 变更说明文件
|
|
360
|
-
* /actor/ 角色目录 / 定义了 Agent 所具有的角色信息,可自定义角色
|
|
361
|
-
* /{name}/stack.md 角色技能(技术栈)说明文件
|
|
362
|
-
* /{name}/limit.md 角色限制说明文件
|
|
363
|
-
* /.archives/ 归档记录
|
|
364
|
-
* /.activities/
|
|
365
|
-
* /actor@plan-XXX/ 角色在某个计划下的历史执行记录(带时间戳)
|
|
366
|
-
* source/ 源代码目录
|
|
367
|
-
* /develop-01
|
|
368
|
-
* /develop-02
|
|
369
|
-
* /develop-03
|
|
370
|
-
* integration/ 集成目录(特殊集成配置)
|
|
371
|
-
* openspec/ OpenSpec 目录
|
|
372
|
-
* /config.json 配置文件
|
|
373
|
-
* spec-kit/ SpecKit 目录
|
|
374
|
-
* kiro/ Kiro 目录
|
|
375
|
-
* trea/ Trea 目录
|
|
376
|
-
* cursor/ Cursor 目录
|
|
377
|
-
* lingma/ Lingma 目录
|
|
378
|
-
* qoder/ Qoder 目录
|
|
379
|
-
* windsurf/ WindSurf 目录
|
|
380
|
-
* github/ Github Copilot 目录
|
|
381
|
-
* claude-code/ Claude Code 目录
|
|
382
|
-
* chatgpt/ ChatGPT 目录
|
|
383
|
-
* auggie/ Auggie (Augment CLI) 目录
|
|
384
|
-
* cline/ Cline 目录
|
|
385
|
-
* roocode/ RooCode 目录
|
|
386
|
-
* codebuddy/ CodeBuddy Code (CLI) 目录
|
|
387
|
-
* costrict/ CoStrict 目录
|
|
388
|
-
* crush/ Crush 目录
|
|
389
|
-
* factory/ Factory Droid 目录
|
|
390
|
-
* gemini/ Gemini CLI 目录
|
|
391
|
-
* opencode/ OpenCode 目录
|
|
392
|
-
* kilo/ Kilo Code 目录
|
|
393
|
-
* codex/ Codex 目录
|
|
394
|
-
* amazonq/ Amazon Q Developer 目录
|
|
395
|
-
* 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
|
+
*
|
|
396
217
|
* @param options
|
|
397
218
|
*/
|
|
398
219
|
module.exports = (options) => {
|
|
@@ -401,17 +222,26 @@ module.exports = (options) => {
|
|
|
401
222
|
/*
|
|
402
223
|
* 基本信息
|
|
403
224
|
*/
|
|
404
|
-
const directory = parsed.
|
|
225
|
+
const directory = parsed.dir || parsed.d || '.';
|
|
405
226
|
// 创建基础目录结构
|
|
406
227
|
const basePath = path.resolve(process.cwd(), directory);
|
|
407
228
|
|
|
408
|
-
Ec.waiting(`准备在目录 "${basePath}" 中初始化
|
|
229
|
+
Ec.waiting(`准备在目录 "${basePath}" 中初始化 R2MO 工程...`);
|
|
409
230
|
|
|
410
231
|
// 创建目录结构
|
|
411
232
|
_ioDirectory(basePath)
|
|
412
233
|
.then(() => _ioFile(basePath))
|
|
413
234
|
.then(() => {
|
|
414
|
-
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('');
|
|
415
245
|
// 关闭 readline 接口
|
|
416
246
|
Ec.askClose();
|
|
417
247
|
// 退出程序
|
|
@@ -424,4 +254,4 @@ module.exports = (options) => {
|
|
|
424
254
|
// 退出程序
|
|
425
255
|
process.exit(1);
|
|
426
256
|
});
|
|
427
|
-
}
|
|
257
|
+
};
|