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.
Files changed (94) hide show
  1. package/.claude/skills/r2mo-rad-lain/SKILL.md +63 -374
  2. package/.trae/skills/algorithmic-art/LICENSE.txt +202 -0
  3. package/.trae/skills/algorithmic-art/SKILL.md +405 -0
  4. package/.trae/skills/algorithmic-art/templates/generator_template.js +223 -0
  5. package/.trae/skills/algorithmic-art/templates/viewer.html +599 -0
  6. package/.trae/skills/doc-coauthoring/SKILL.md +375 -0
  7. package/.trae/skills/frontend-design/LICENSE.txt +177 -0
  8. package/.trae/skills/frontend-design/SKILL.md +42 -0
  9. package/.trae/skills/r2mo-rad-lain/SKILL.md +101 -0
  10. package/README.md +9 -32
  11. package/docs/images/r2mo-lain.png +0 -0
  12. package/package.json +11 -11
  13. package/skills/r2mo-rad-domain/SKILL.md +70 -0
  14. package/src/_skill/repositories.json +9 -3
  15. package/src/_template/LAIN/.obsidian/app.json +1 -0
  16. package/src/_template/LAIN/.obsidian/appearance.json +10 -0
  17. package/src/_template/LAIN/.obsidian/community-plugins.json +7 -0
  18. package/src/_template/LAIN/.obsidian/core-plugins.json +33 -0
  19. package/src/_template/LAIN/.obsidian/plugins/dataview/main.js +20876 -0
  20. package/src/_template/LAIN/.obsidian/plugins/dataview/manifest.json +11 -0
  21. package/src/_template/LAIN/.obsidian/plugins/dataview/styles.css +141 -0
  22. package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/data.json +815 -0
  23. package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/main.js +10 -0
  24. package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/manifest.json +12 -0
  25. package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/styles.css +1 -0
  26. package/src/_template/LAIN/.obsidian/plugins/obsidian-kanban/main.js +153 -0
  27. package/src/_template/LAIN/.obsidian/plugins/obsidian-kanban/manifest.json +11 -0
  28. package/src/_template/LAIN/.obsidian/plugins/obsidian-kanban/styles.css +1 -0
  29. package/src/_template/LAIN/.obsidian/plugins/obsidian-plantuml/main.js +7732 -0
  30. package/src/_template/LAIN/.obsidian/plugins/obsidian-plantuml/manifest.json +10 -0
  31. package/src/_template/LAIN/.obsidian/plugins/obsidian-plantuml/styles.css +38 -0
  32. package/src/_template/LAIN/.obsidian/plugins/obsidian-tasks-plugin/main.js +504 -0
  33. package/src/_template/LAIN/.obsidian/plugins/obsidian-tasks-plugin/manifest.json +12 -0
  34. package/src/_template/LAIN/.obsidian/plugins/obsidian-tasks-plugin/styles.css +1 -0
  35. package/src/_template/LAIN/.obsidian/snippets/body-font.css +27 -0
  36. package/src/_template/LAIN/.obsidian/themes/Primary/manifest.json +9 -0
  37. package/src/_template/LAIN/.obsidian/themes/Primary/theme.css +3878 -0
  38. package/src/_template/LAIN/.obsidian/themes/Retro Windows/manifest.json +7 -0
  39. package/src/_template/LAIN/.obsidian/themes/Retro Windows/theme.css +582 -0
  40. package/src/_template/LAIN/.obsidian/themes/RetroOS 98/manifest.json +9 -0
  41. package/src/_template/LAIN/.obsidian/themes/RetroOS 98/theme.css +2566 -0
  42. package/src/_template/LAIN/.obsidian/types.json +28 -0
  43. package/src/_template/LAIN/.obsidian/workspace.json +184 -0
  44. package/src/_template/LAIN/AGENTS.md +170 -16
  45. package/src/_template/R2MO/domain-enhance.md +10 -0
  46. package/src/commander/app.json +13 -0
  47. package/src/commander/apply.json +13 -0
  48. package/src/commander/ask.json +6 -0
  49. package/src/commander/docs.json +13 -0
  50. package/src/commander/domain.json +19 -0
  51. package/src/commander/init.json +1 -1
  52. package/src/commander/mmr0.json +6 -0
  53. package/src/commander/mmr2.json +6 -0
  54. package/src/executor/executeApp.js +133 -0
  55. package/src/executor/{executeSkills.js → executeApply.js} +166 -302
  56. package/src/executor/executeAsk.js +274 -0
  57. package/src/executor/executeDocs.js +498 -0
  58. package/src/executor/executeDomain.js +293 -0
  59. package/src/executor/executeInit.js +159 -383
  60. package/src/executor/executeMcp.js +74 -1
  61. package/src/executor/executeMmr0.js +488 -0
  62. package/src/executor/executeMmr2.js +880 -0
  63. package/src/executor/index.js +15 -3
  64. package/src/python/r2mo_proto.py +418 -0
  65. package/src/python/r2mo_proto_database.py +369 -0
  66. package/src/python/r2mo_proto_domain.py +458 -0
  67. package/src/utils/momo-menu.js +43 -13
  68. package/.claude/skills/r2mo-rad-lain/PROMPT.md +0 -281
  69. package/.claude/skills/r2mo-rad-lain/README.md +0 -192
  70. package/.claude/skills/r2mo-rad-lain/examples/argument-parsing.js +0 -154
  71. package/.claude/skills/r2mo-rad-lain/examples/file-operations.js +0 -182
  72. package/.claude/skills/r2mo-rad-lain/file-utils-api.md +0 -281
  73. package/.claude/skills/r2mo-rad-lain/menu-api.md +0 -187
  74. package/.claude/skills/r2mo-rad-lain/scripts/file-utils.js +0 -223
  75. package/.claude/skills/r2mo-rad-lain/scripts/menu.js +0 -289
  76. package/.claude/skills/r2mo-rad-lain/scripts/yaml-parser.js +0 -209
  77. package/.claude/skills/r2mo-rad-lain/templates/command.json.template +0 -13
  78. package/.claude/skills/r2mo-rad-lain/templates/executor.js.template +0 -32
  79. package/.claude/skills/r2mo-rad-lain/templates/interactive-menu.js.template +0 -221
  80. package/src/_template/LAIN/.momo/advanced/actor.md +0 -42
  81. package/src/_template/LAIN/.momo/advanced/refer.json +0 -46
  82. package/src/_template/LAIN/.momo/scripts/submodule-clean.sh +0 -56
  83. package/src/_template/LAIN/changes/proposal.md +0 -39
  84. package/src/_template/LAIN/changes/tasks/task-detail.md +0 -45
  85. package/src/_template/LAIN/changes/tasks.md +0 -49
  86. package/src/_template/LAIN/execute/admin-n-f-dashboard.md +0 -53
  87. package/src/_template/LAIN/execute/admin-n-f-form.md +0 -51
  88. package/src/_template/LAIN/execute/admin-n-f-home.md +0 -49
  89. package/src/_template/LAIN/execute/admin-n-f-list.md +0 -52
  90. package/src/_template/LAIN/execute/admin-n-f-login.md +0 -56
  91. package/src/_template/LAIN/specification/project-model.md +0 -13
  92. package/src/_template/LAIN/specification/project.md +0 -73
  93. package/src/_template/LAIN/specification/requirement.md +0 -25
  94. 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
- ".momo/prompt/",
9
- ".momo/template/",
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
- results.push(true);
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
- return results;
64
- }
38
+ };
65
39
 
40
+ /**
41
+ * 创建和拷贝文件
42
+ * @param {string} baseDir 基础目录
43
+ */
66
44
  const _ioFile = async (baseDir) => {
67
- /*
68
- * 填充空文件目录
69
- * .momo/prompt/
70
- * .momo/template/ 存放模板目录
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
- // 检查 actor.md 文件
127
- const actorSourcePath = path.resolve(templateDir, ".momo/advanced/actor.md");
128
- const actorTargetPath = path.resolve(baseDir, ".momo/advanced/actor.md");
129
-
130
- if (fs.existsSync(actorSourcePath) && fs.existsSync(actorTargetPath)) {
131
- hasExistingFiles = true;
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
- if (!fs.existsSync(fullPath)) {
193
- Ec.waiting("创建空文件:" + fullPath);
194
- await fsAsync.writeFile(fullPath, "");
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
- // 为每个 integration 目录创建 config.json 文件
199
- const integrationDirs = [
200
- ".momo/integration/openspec/",
201
- ".momo/integration/spec-kit/",
202
- ".momo/integration/kiro/",
203
- ".momo/integration/trea/",
204
- ".momo/integration/cursor/",
205
- ".momo/integration/lingma/",
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
- // 为每个 integration 目录创建 config.json
241
- for (const dir of integrationDirs) {
242
- const configPath = path.resolve(baseDir, dir, "config.json");
243
- // 确保目录存在
244
- const dirPath = path.dirname(configPath);
245
- if (!fs.existsSync(dirPath)) {
246
- await fsAsync.mkdir(dirPath, {recursive: true});
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
- // 如果文件不存在或用户选择覆盖,则创建 config.json
250
- if (!fs.existsSync(configPath) || shouldOverwrite) {
251
- // 根据目录名设置 llm 字段
252
- const dirName = path.basename(dir);
253
- const config = {...configTemplate, llm: dirName};
254
-
255
- Ec.waiting("创建配置文件:" + configPath);
256
- await fsAsync.writeFile(configPath, JSON.stringify(config, null, 4));
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("跳过文件:" + configPath);
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
- for (const file of templateFiles) {
264
- const sourcePath = path.resolve(templateDir, file.source);
265
- const targetPath = path.resolve(baseDir, file.target);
266
-
267
- // 检查源文件是否存在
268
- if (fs.existsSync(sourcePath)) {
269
- // 确保目标目录存在
270
- const targetDir = path.dirname(targetPath);
271
- if (!fs.existsSync(targetDir)) {
272
- await fsAsync.mkdir(targetDir, {recursive: true});
273
- }
274
-
275
- // 检查目标文件是否已存在
276
- if (fs.existsSync(targetPath)) {
277
- // 跳过 .momo/specification/ 目录下的所有文件
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("跳过文件:" + file.target);
133
+ Ec.waiting("跳过目录:.r2mo/.obsidian");
286
134
  }
287
135
  } else {
288
- Ec.waiting("拷贝模板文件:" + file.target);
289
- await fsAsync.copyFile(sourcePath, targetPath);
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
- // 拷贝 .momo/advanced/actor.md 文件
306
- if (fs.existsSync(actorSourcePath)) {
307
- // 确保目标目录存在
308
- const actorDir = path.dirname(actorTargetPath);
309
- if (!fs.existsSync(actorDir)) {
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
- if (fs.existsSync(actorTargetPath)) {
315
- // 目标文件已存在,根据用户选择决定是否覆盖
316
- if (shouldOverwrite) {
317
- Ec.waiting("覆盖模板文件:" + ".momo/advanced/actor.md");
318
- await fsAsync.copyFile(actorSourcePath, actorTargetPath);
319
- } else {
320
- Ec.waiting("跳过文件:.momo/advanced/actor.md");
321
- }
322
- } else {
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
- // 确保 .momo/advanced/ 目录存在
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
- // 检查 .momo 是否已经在 .gitignore 中
357
- if (!gitignoreContent.includes('.momo')) {
176
+ // 检查 .r2mo 是否已经在 .gitignore 中
177
+ if (!gitignoreContent.includes('.r2mo')) {
358
178
  // 如果内容不为空,先添加一个换行
359
179
  if (gitignoreContent && !gitignoreContent.endsWith('\n')) {
360
180
  gitignoreContent += '\n';
361
181
  }
362
- // 添加 .momo 到 .gitignore
363
- gitignoreContent += '.momo\n';
182
+ // 添加 .r2mo 到 .gitignore
183
+ gitignoreContent += '.r2mo\n';
364
184
 
365
185
  // 写入 .gitignore 文件
366
186
  await fsAsync.writeFile(gitignorePath, gitignoreContent);
367
- Ec.waiting("已更新 .gitignore 文件,添加 .momo 目录到忽略列表");
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(".claude/skills/ 目录中的 .placeholder 文件已存在");
189
+ Ec.waiting(".gitignore 文件已包含 .r2mo 目录,无需更新");
384
190
  }
385
- }
191
+ };
386
192
 
387
193
  /**
388
194
  * 目录结构说明
389
- * .momo/ 根目录
390
- * /prompt/ 存放备注目录
391
- * /template/ 存放模板目录
392
- * /advanced/ 高级配置说明
393
- * /actor.md - 角色定制说明
394
- * /scripts/ 特殊脚本目录
395
- * /reference/
396
- * /maven/ Maven 外置项目
397
- * /npm/ NPM 外置项目
398
- * /specification/ 工作目录
399
- * /project.md - 项目基本说明文件
400
- * /project-model.md - 模型说明文件(通常是建模所需的核心概念模型)
401
- *
402
- * --------- 需求分析 / 任务分配设计 / 角色定制 ------------
403
- * /requirement.md - 基础需求说明(第一份)
404
- * /changes/ 变更详情
405
- * /change-XXX/
406
- * /implementation.md 实现方案步骤说明
407
- * /proposal.md ( OpenSpec ) 变更提案说明文件
408
- * /tasks.md ( OpenSpec ) 变更任务列表说明 -> 链接到 tasks/ 目录下的具体任务清单中
409
- * /tasks 计划详细内容,如果拆分成 Task 则对应到 Tasks/plan-XXX/ 目录中
410
- * /M01-T001-01.md 具体任务文件,M代表模块,T代表任务,001代表任务编号,01代表子任务序号
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.directory || parsed.d || '.';
225
+ const directory = parsed.dir || parsed.d || '.';
459
226
  // 创建基础目录结构
460
227
  const basePath = path.resolve(process.cwd(), directory);
461
228
 
462
- Ec.waiting(`准备在目录 "${basePath}" 中初始化 Spec 工程...`);
229
+ Ec.waiting(`准备在目录 "${basePath}" 中初始化 R2MO 工程...`);
463
230
 
464
231
  // 创建目录结构
465
232
  _ioDirectory(basePath)
466
233
  .then(() => _ioFile(basePath))
467
234
  .then(() => {
468
- Ec.info('✅ SPEC / 项目初始化完成!');
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
+ };