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.
Files changed (162) hide show
  1. package/.claude/skills/algorithmic-art/LICENSE.txt +202 -0
  2. package/.claude/skills/algorithmic-art/SKILL.md +405 -0
  3. package/.claude/skills/algorithmic-art/templates/generator_template.js +223 -0
  4. package/.claude/skills/algorithmic-art/templates/viewer.html +599 -0
  5. package/.claude/skills/r2mo-rad-lain/SKILL.md +101 -0
  6. package/.cursor/mcp.json +17 -0
  7. package/.obsidian/app.json +1 -0
  8. package/.obsidian/appearance.json +4 -0
  9. package/.obsidian/community-plugins.json +4 -0
  10. package/.obsidian/core-plugins.json +33 -0
  11. package/.obsidian/plugins/ai-agent/main.js +98495 -0
  12. package/.obsidian/plugins/ai-agent/manifest.json +11 -0
  13. package/.obsidian/plugins/ai-agent/styles.css +806 -0
  14. package/.obsidian/plugins/dataview/main.js +20876 -0
  15. package/.obsidian/plugins/dataview/manifest.json +11 -0
  16. package/.obsidian/plugins/dataview/styles.css +141 -0
  17. package/.obsidian/plugins/obsidian-excalidraw-plugin/main.js +10 -0
  18. package/.obsidian/plugins/obsidian-excalidraw-plugin/manifest.json +12 -0
  19. package/.obsidian/plugins/obsidian-excalidraw-plugin/styles.css +1 -0
  20. package/.obsidian/plugins/templater-obsidian/main.js +45 -0
  21. package/.obsidian/plugins/templater-obsidian/manifest.json +11 -0
  22. package/.obsidian/plugins/templater-obsidian/styles.css +226 -0
  23. package/.obsidian/plugins/terminal/main.js +200 -0
  24. package/.obsidian/plugins/terminal/manifest.json +14 -0
  25. package/.obsidian/plugins/terminal/styles.css +32 -0
  26. package/.obsidian/themes/AnuPpuccin/manifest.json +7 -0
  27. package/.obsidian/themes/AnuPpuccin/theme.css +9080 -0
  28. package/.obsidian/themes/Things/manifest.json +7 -0
  29. package/.obsidian/themes/Things/theme.css +1628 -0
  30. package/.obsidian/workspace.json +196 -0
  31. package/.trae/skills/algorithmic-art/LICENSE.txt +202 -0
  32. package/.trae/skills/algorithmic-art/SKILL.md +405 -0
  33. package/.trae/skills/algorithmic-art/templates/generator_template.js +223 -0
  34. package/.trae/skills/algorithmic-art/templates/viewer.html +599 -0
  35. package/.trae/skills/doc-coauthoring/SKILL.md +375 -0
  36. package/.trae/skills/frontend-design/LICENSE.txt +177 -0
  37. package/.trae/skills/frontend-design/SKILL.md +42 -0
  38. package/.trae/skills/r2mo-rad-lain/SKILL.md +101 -0
  39. package/README.md +12 -148
  40. package/docs/images/logo.jpeg +0 -0
  41. package/docs/images/r2mo-lain.png +0 -0
  42. package/install.sh +1 -0
  43. package/package.json +15 -11
  44. package/skills/r2mo-rad-domain/SKILL.md +70 -0
  45. package/skills/r2mo-rad-lain/SKILL.md +101 -0
  46. package/src/_mcp/skills-server.mjs +70 -0
  47. package/src/_skill/repositories.json +22 -0
  48. package/src/_template/LAIN/.obsidian/app.json +1 -0
  49. package/src/_template/LAIN/.obsidian/appearance.json +10 -0
  50. package/src/_template/LAIN/.obsidian/community-plugins.json +7 -0
  51. package/src/_template/LAIN/.obsidian/core-plugins.json +33 -0
  52. package/src/_template/LAIN/.obsidian/plugins/dataview/main.js +20876 -0
  53. package/src/_template/LAIN/.obsidian/plugins/dataview/manifest.json +11 -0
  54. package/src/_template/LAIN/.obsidian/plugins/dataview/styles.css +141 -0
  55. package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/data.json +815 -0
  56. package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/main.js +10 -0
  57. package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/manifest.json +12 -0
  58. package/src/_template/LAIN/.obsidian/plugins/obsidian-excalidraw-plugin/styles.css +1 -0
  59. package/src/_template/LAIN/.obsidian/plugins/obsidian-kanban/main.js +153 -0
  60. package/src/_template/LAIN/.obsidian/plugins/obsidian-kanban/manifest.json +11 -0
  61. package/src/_template/LAIN/.obsidian/plugins/obsidian-kanban/styles.css +1 -0
  62. package/src/_template/LAIN/.obsidian/plugins/obsidian-plantuml/main.js +7732 -0
  63. package/src/_template/LAIN/.obsidian/plugins/obsidian-plantuml/manifest.json +10 -0
  64. package/src/_template/LAIN/.obsidian/plugins/obsidian-plantuml/styles.css +38 -0
  65. package/src/_template/LAIN/.obsidian/plugins/obsidian-tasks-plugin/main.js +504 -0
  66. package/src/_template/LAIN/.obsidian/plugins/obsidian-tasks-plugin/manifest.json +12 -0
  67. package/src/_template/LAIN/.obsidian/plugins/obsidian-tasks-plugin/styles.css +1 -0
  68. package/src/_template/LAIN/.obsidian/snippets/body-font.css +27 -0
  69. package/src/_template/LAIN/.obsidian/themes/Primary/manifest.json +9 -0
  70. package/src/_template/LAIN/.obsidian/themes/Primary/theme.css +3878 -0
  71. package/src/_template/LAIN/.obsidian/themes/Retro Windows/manifest.json +7 -0
  72. package/src/_template/LAIN/.obsidian/themes/Retro Windows/theme.css +582 -0
  73. package/src/_template/LAIN/.obsidian/themes/RetroOS 98/manifest.json +9 -0
  74. package/src/_template/LAIN/.obsidian/themes/RetroOS 98/theme.css +2566 -0
  75. package/src/_template/LAIN/.obsidian/types.json +28 -0
  76. package/src/_template/LAIN/.obsidian/workspace.json +184 -0
  77. package/src/_template/LAIN/AGENTS.md +170 -16
  78. package/src/_template/R2MO/domain-enhance.md +10 -0
  79. package/src/commander/app.json +13 -0
  80. package/src/commander/apply.json +13 -0
  81. package/src/commander/ask.json +6 -0
  82. package/src/commander/docs.json +13 -0
  83. package/src/commander/domain.json +19 -0
  84. package/src/commander/help.json +5 -0
  85. package/src/commander/init.json +1 -1
  86. package/src/commander/mcp.json +13 -0
  87. package/src/commander/mmr0.json +6 -0
  88. package/src/commander/mmr2.json +6 -0
  89. package/src/commander/open.json +8 -2
  90. package/src/executor/executeApp.js +133 -0
  91. package/src/executor/executeApply.js +611 -0
  92. package/src/executor/executeAsk.js +274 -0
  93. package/src/executor/executeDocs.js +498 -0
  94. package/src/executor/executeDomain.js +293 -0
  95. package/src/executor/executeEnv.js +48 -38
  96. package/src/executor/executeHelp.js +77 -16
  97. package/src/executor/executeInit.js +176 -346
  98. package/src/executor/executeMcp.js +363 -0
  99. package/src/executor/executeMmr0.js +488 -0
  100. package/src/executor/executeMmr2.js +880 -0
  101. package/src/executor/executeOpen.js +144 -125
  102. package/src/executor/index.js +17 -39
  103. package/src/momo.js +2 -1
  104. package/src/python/r2mo_proto.py +418 -0
  105. package/src/python/r2mo_proto_database.py +369 -0
  106. package/src/python/r2mo_proto_domain.py +458 -0
  107. package/src/utils/momo-args.js +39 -0
  108. package/src/utils/momo-file-utils.js +75 -0
  109. package/src/utils/momo-menu.js +84 -0
  110. package/src/_template/LAIN/.momo/advanced/actor.md +0 -42
  111. package/src/_template/LAIN/.momo/advanced/refer.json +0 -46
  112. package/src/_template/LAIN/.momo/scripts/submodule-clean.sh +0 -56
  113. package/src/_template/LAIN/changes/proposal.md +0 -39
  114. package/src/_template/LAIN/changes/tasks/task-detail.md +0 -45
  115. package/src/_template/LAIN/changes/tasks.md +0 -49
  116. package/src/_template/LAIN/execute/admin-n-f-dashboard.md +0 -53
  117. package/src/_template/LAIN/execute/admin-n-f-form.md +0 -51
  118. package/src/_template/LAIN/execute/admin-n-f-home.md +0 -49
  119. package/src/_template/LAIN/execute/admin-n-f-list.md +0 -52
  120. package/src/_template/LAIN/execute/admin-n-f-login.md +0 -56
  121. package/src/_template/LAIN/specification/project-model.md +0 -13
  122. package/src/_template/LAIN/specification/project.md +0 -73
  123. package/src/_template/LAIN/specification/requirement.md +0 -25
  124. package/src/commander/actor.json +0 -12
  125. package/src/commander/actors.json +0 -6
  126. package/src/commander/add.json +0 -12
  127. package/src/commander/agent.json +0 -12
  128. package/src/commander/agentcfg.json +0 -5
  129. package/src/commander/archive.json +0 -12
  130. package/src/commander/commit.json +0 -12
  131. package/src/commander/console.json +0 -7
  132. package/src/commander/lain.json +0 -7
  133. package/src/commander/list.json +0 -7
  134. package/src/commander/plan.json +0 -12
  135. package/src/commander/project.json +0 -12
  136. package/src/commander/pull.json +0 -6
  137. package/src/commander/push.json +0 -6
  138. package/src/commander/repo.json +0 -18
  139. package/src/commander/run.json +0 -18
  140. package/src/commander/show.json +0 -12
  141. package/src/commander/tasks.json +0 -18
  142. package/src/commander/unlock.json +0 -6
  143. package/src/commander/validate.json +0 -12
  144. package/src/executor/executeActor.js +0 -133
  145. package/src/executor/executeActors.js +0 -58
  146. package/src/executor/executeAdd.js +0 -307
  147. package/src/executor/executeAgent.js +0 -299
  148. package/src/executor/executeAgentCfg.js +0 -210
  149. package/src/executor/executeArchive.js +0 -124
  150. package/src/executor/executeCommit.js +0 -202
  151. package/src/executor/executeConsole.js +0 -142
  152. package/src/executor/executeList.js +0 -133
  153. package/src/executor/executePlan.js +0 -164
  154. package/src/executor/executeProject.js +0 -313
  155. package/src/executor/executePull.js +0 -127
  156. package/src/executor/executePush.js +0 -243
  157. package/src/executor/executeRepo.js +0 -238
  158. package/src/executor/executeRun.js +0 -644
  159. package/src/executor/executeShow.js +0 -164
  160. package/src/executor/executeTasks.js +0 -384
  161. package/src/executor/executeUnlock.js +0 -110
  162. 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
- ".momo/prompt/",
9
- ".momo/template/",
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
- 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
+ }
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
- return results;
49
- }
38
+ };
50
39
 
40
+ /**
41
+ * 创建和拷贝文件
42
+ * @param {string} baseDir 基础目录
43
+ */
51
44
  const _ioFile = async (baseDir) => {
52
- /*
53
- * 填充空文件目录
54
- * .momo/prompt/
55
- * .momo/template/ 存放模板目录
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 emptyFileDirs = [
133
- ".momo/prompt/",
134
- ".momo/template/",
135
- ".momo/scripts/",
136
- "reference/maven",
137
- "reference/npm",
138
- "specification/changes/",
139
- "specification/actor/",
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
- if (!fs.existsSync(fullPath)) {
180
- Ec.waiting("创建空文件:" + fullPath);
181
- 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);
182
74
  }
75
+ } else {
76
+ Ec.warn(`⚠ 模板文件不存在: ${agentsSourcePath}`);
183
77
  }
184
78
 
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
- };
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
- // 为每个 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});
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
- // 如果文件不存在或用户选择覆盖,则创建 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));
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("跳过文件:" + configPath);
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
- for (const file of templateFiles) {
251
- const sourcePath = path.resolve(templateDir, file.source);
252
- const targetPath = path.resolve(baseDir, file.target);
253
-
254
- // 检查源文件是否存在
255
- if (fs.existsSync(sourcePath)) {
256
- // 确保目标目录存在
257
- const targetDir = path.dirname(targetPath);
258
- if (!fs.existsSync(targetDir)) {
259
- await fsAsync.mkdir(targetDir, {recursive: true});
260
- }
261
-
262
- // 检查目标文件是否已存在
263
- if (fs.existsSync(targetPath)) {
264
- // 跳过 specification/ 目录下的所有文件
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("跳过文件:" + file.target);
133
+ Ec.waiting("跳过目录:.r2mo/.obsidian");
273
134
  }
274
135
  } else {
275
- Ec.waiting("拷贝模板文件:" + file.target);
276
- await fsAsync.copyFile(sourcePath, targetPath);
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
- // 拷贝 .momo/advanced/actor.md 文件
293
- if (fs.existsSync(actorSourcePath)) {
294
- // 确保目标目录存在
295
- const actorDir = path.dirname(actorTargetPath);
296
- if (!fs.existsSync(actorDir)) {
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
- if (fs.existsSync(actorTargetPath)) {
302
- // 目标文件已存在,根据用户选择决定是否覆盖
303
- if (shouldOverwrite) {
304
- Ec.waiting("覆盖模板文件:" + ".momo/advanced/actor.md");
305
- await fsAsync.copyFile(actorSourcePath, actorTargetPath);
306
- } else {
307
- Ec.waiting("跳过文件:.momo/advanced/actor.md");
308
- }
309
- } else {
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
- // 确保 .momo/advanced/ 目录存在
328
- const momoAdvancedDir = path.resolve(baseDir, ".momo/advanced/");
329
- if (!fs.existsSync(momoAdvancedDir)) {
330
- await fsAsync.mkdir(momoAdvancedDir, {recursive: true});
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
- * .momo/prompt/ 存放备注目录
337
- * .momo/template/ 存放模板目录
338
- * .momo/advanced/ 高级配置说明
339
- * /actor.md - 角色定制说明
340
- * .momo/scripts/ 特殊脚本目录
341
- * reference/
342
- * /maven/ Maven 外置项目
343
- * /npm/ NPM 外置项目
344
- * specification/ 工作目录
345
- * /project.md - 项目基本说明文件
346
- * /project-model.md - 模型说明文件(通常是建模所需的核心概念模型)
347
- *
348
- * --------- 需求分析 / 任务分配设计 / 角色定制 ------------
349
- * /requirement.md - 基础需求说明(第一份)
350
- * /changes/ 变更详情
351
- * /change-XXX/
352
- * /implementation.md 实现方案步骤说明
353
- * /proposal.md ( OpenSpec ) 变更提案说明文件
354
- * /tasks.md ( OpenSpec ) 变更任务列表说明 -> 链接到 tasks/ 目录下的具体任务清单中
355
- * /tasks 计划详细内容,如果拆分成 Task 则对应到 Tasks/plan-XXX/ 目录中
356
- * /M01-T001-01.md 具体任务文件,M代表模块,T代表任务,001代表任务编号,01代表子任务序号
357
- * /M01-T001-02.md ......
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.directory || '.';
225
+ const directory = parsed.dir || parsed.d || '.';
405
226
  // 创建基础目录结构
406
227
  const basePath = path.resolve(process.cwd(), directory);
407
228
 
408
- Ec.waiting(`准备在目录 "${basePath}" 中初始化 Spec 工程...`);
229
+ Ec.waiting(`准备在目录 "${basePath}" 中初始化 R2MO 工程...`);
409
230
 
410
231
  // 创建目录结构
411
232
  _ioDirectory(basePath)
412
233
  .then(() => _ioFile(basePath))
413
234
  .then(() => {
414
- 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('');
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
+ };