momo-ai 1.0.0 → 1.0.2

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 (107) hide show
  1. package/README.md +117 -0
  2. package/package.json +1 -1
  3. package/src/_template/LAIN/.momo/advanced/actor.md +42 -0
  4. package/src/_template/LAIN/.momo/scripts/submodule-clean.sh +56 -0
  5. package/src/_template/LAIN/AGENTS.md +103 -0
  6. package/src/_template/LAIN/changes/proposal.md +39 -0
  7. package/src/_template/LAIN/changes/tasks/task-detail.md +45 -0
  8. package/src/_template/LAIN/changes/tasks.md +49 -0
  9. package/src/_template/LAIN/execute/admin-n-f-dashboard.md +53 -0
  10. package/src/_template/LAIN/execute/admin-n-f-form.md +51 -0
  11. package/src/_template/LAIN/execute/admin-n-f-home.md +49 -0
  12. package/src/_template/LAIN/execute/admin-n-f-list.md +52 -0
  13. package/src/_template/LAIN/execute/admin-n-f-login.md +56 -0
  14. package/src/_template/LAIN/specification/project-model.md +13 -0
  15. package/src/_template/LAIN/specification/project.md +73 -0
  16. package/src/_template/LAIN/specification/requirement.md +25 -0
  17. package/src/_template/PROMPT/add.md.ejs +5 -0
  18. package/src/_template/PROMPT/plan.md.ejs +5 -0
  19. package/src/_template/PROMPT/run.md.ejs +9 -0
  20. package/src/_template/PROMPT/tasks.md.ejs +5 -0
  21. package/src/commander/actor.json +12 -0
  22. package/src/commander/actors.json +6 -0
  23. package/src/commander/add.json +12 -0
  24. package/src/commander/archive.json +12 -0
  25. package/src/commander/env.json +7 -0
  26. package/src/commander/help.json +7 -0
  27. package/src/commander/init.json +13 -0
  28. package/src/commander/list.json +7 -0
  29. package/src/commander/open.json +7 -0
  30. package/src/commander/plan.json +12 -0
  31. package/src/commander/repo.json +18 -0
  32. package/src/commander/run.json +18 -0
  33. package/src/commander/show.json +12 -0
  34. package/src/commander/tasks.json +18 -0
  35. package/src/commander/unlock.json +6 -0
  36. package/src/commander/validate.json +12 -0
  37. package/src/epic/history/ai.economy.impl.fn.excel.js +148 -0
  38. package/src/epic/history/ai.economy.impl.fn.execute.js +117 -0
  39. package/src/epic/history/ai.economy.impl.fn.java.js +140 -0
  40. package/src/epic/history/ai.economy.impl.fn.json.js +23 -0
  41. package/src/epic/history/ai.economy.impl.fn.plugin.js +160 -0
  42. package/src/epic/history/ai.economy.impl.fn.react.js +219 -0
  43. package/src/epic/history/ai.export.impl.fn.parse.js +345 -0
  44. package/src/epic/history/ai.export.impl.fn.seek.js +42 -0
  45. package/src/epic/history/ai.export.interface.fn.string.js +5 -0
  46. package/src/epic/history/ai.export.interface.io.js +69 -0
  47. package/src/epic/history/ai.export.interface.util.js +17 -0
  48. package/src/epic/history/ai.parse.metadata.js +94 -0
  49. package/src/epic/history/ai.path.fn.dir.operation.js +87 -0
  50. package/src/epic/history/ai.path.fn.io.command.js +43 -0
  51. package/src/epic/history/ai.path.fn.io.specification.js +59 -0
  52. package/src/epic/history/ai.path.fn.io.typed.js +63 -0
  53. package/src/epic/history/ai.path.fn.out.content.js +47 -0
  54. package/src/epic/history/ai.string.fn.str.util.js +63 -0
  55. package/src/epic/history/ai.uncork.fn.element.feature.js +52 -0
  56. package/src/epic/history/ai.uncork.fn.it.feature.js +118 -0
  57. package/src/epic/history/ai.uncork.fn.to.typed.js +74 -0
  58. package/src/epic/history/ai.under.fn.cx.evaluate.js +81 -0
  59. package/src/epic/history/ai.under.fn.fx.terminal.js +143 -0
  60. package/src/epic/history/ai.unified.fn.fn.error.code.js +108 -0
  61. package/src/epic/history/ai.unified.fn.is.decision.js +20 -0
  62. package/src/epic/history/ai.unified.fn.sorter.element.js +26 -0
  63. package/src/epic/history/zero.__.fn.find.util.js +37 -0
  64. package/src/epic/history/zero.__.v.constant.js +5 -0
  65. package/src/epic/index.js +50 -0
  66. package/src/epic/lain.fn.execute.js +116 -0
  67. package/src/epic/lain.fn.parse.js +94 -0
  68. package/src/epic/momo.fn.cx.js +81 -0
  69. package/src/epic/momo.fn.dir.js +87 -0
  70. package/src/epic/momo.fn.element.js +52 -0
  71. package/src/epic/momo.fn.find.js +37 -0
  72. package/src/epic/momo.fn.fx.js +143 -0
  73. package/src/epic/momo.fn.io.js +157 -0
  74. package/src/epic/momo.fn.is.js +20 -0
  75. package/src/epic/momo.fn.it.js +118 -0
  76. package/src/epic/momo.fn.log.js +50 -0
  77. package/src/epic/momo.fn.out.js +47 -0
  78. package/src/epic/momo.fn.sorter.js +26 -0
  79. package/src/epic/momo.fn.str.js +63 -0
  80. package/src/epic/momo.fn.to.js +74 -0
  81. package/src/epic/momo.v.constant.js +5 -0
  82. package/src/epic/momo.v.errorcode.js +108 -0
  83. package/src/executor/executeActor.js +113 -0
  84. package/src/executor/executeActors.js +58 -0
  85. package/src/executor/executeAdd.js +248 -0
  86. package/src/executor/executeArchive.js +124 -0
  87. package/src/executor/executeEnv.js +158 -0
  88. package/src/executor/executeHelp.js +23 -0
  89. package/src/executor/executeInit.js +321 -0
  90. package/src/executor/executeList.js +111 -0
  91. package/src/executor/executeOpen.js +150 -0
  92. package/src/executor/executePlan.js +134 -0
  93. package/src/executor/executeRepo.js +234 -0
  94. package/src/executor/executeRun.js +527 -0
  95. package/src/executor/executeShow.js +164 -0
  96. package/src/executor/executeTasks.js +323 -0
  97. package/src/executor/executeUnlock.js +110 -0
  98. package/src/executor/executeValidate.js +210 -0
  99. package/src/executor/index.js +35 -0
  100. package/src/momo.js +39 -1
  101. package/.idea/copilot.data.migration.agent.xml +0 -6
  102. package/.idea/copilot.data.migration.ask.xml +0 -6
  103. package/.idea/copilot.data.migration.ask2agent.xml +0 -6
  104. package/.idea/copilot.data.migration.edit.xml +0 -6
  105. package/.idea/modules.xml +0 -8
  106. package/.idea/r2mo-lain.iml +0 -12
  107. package/.idea/vcs.xml +0 -6
@@ -0,0 +1,124 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const util = require('util');
4
+ const Ec = require('../epic');
5
+ const fsAsync = require('fs').promises;
6
+
7
+ /**
8
+ * 检查需求目录是否存在
9
+ * @param {string} changesDir changes目录路径
10
+ * @param {string} requirementName 需求名称
11
+ * @returns {boolean} 是否存在
12
+ */
13
+ const _isRequirementExists = (changesDir, requirementName) => {
14
+ const requirementDir = path.join(changesDir, requirementName);
15
+ return fs.existsSync(requirementDir);
16
+ };
17
+
18
+ /**
19
+ * 递归拷贝目录
20
+ * @param {string} src 源目录
21
+ * @param {string} dest 目标目录
22
+ */
23
+ const _copyDir = async (src, dest) => {
24
+ const entries = await fsAsync.readdir(src, { withFileTypes: true });
25
+ await fsAsync.mkdir(dest, { recursive: true });
26
+
27
+ for (let entry of entries) {
28
+ const srcPath = path.join(src, entry.name);
29
+ const destPath = path.join(dest, entry.name);
30
+
31
+ if (entry.isDirectory()) {
32
+ await _copyDir(srcPath, destPath);
33
+ } else {
34
+ await fsAsync.copyFile(srcPath, destPath);
35
+ }
36
+ }
37
+ };
38
+
39
+ /**
40
+ * 递归删除目录
41
+ * @param {string} dirPath 目录路径
42
+ */
43
+ const _removeDir = async (dirPath) => {
44
+ const entries = await fsAsync.readdir(dirPath, { withFileTypes: true });
45
+
46
+ for (let entry of entries) {
47
+ const entryPath = path.join(dirPath, entry.name);
48
+
49
+ if (entry.isDirectory()) {
50
+ await _removeDir(entryPath);
51
+ } else {
52
+ await fsAsync.unlink(entryPath);
53
+ }
54
+ }
55
+
56
+ await fsAsync.rmdir(dirPath);
57
+ };
58
+
59
+ module.exports = async (options) => {
60
+ // 参数提取
61
+ const parsed = Ec.parseArgument(options);
62
+
63
+ // 获取需求名称
64
+ const requirementName = parsed.name || parsed.n;
65
+
66
+ // 验证参数
67
+ if (!requirementName) {
68
+ Ec.error("❌ 请提供需求名称 (-n, --name)");
69
+ process.exit(1);
70
+ }
71
+
72
+ // 检查需求名称是否包含点号(扩展名)
73
+ if (requirementName.includes('.')) {
74
+ Ec.error("❌ 需求名称不能包含点号(.),以防止与文件扩展名混淆");
75
+ process.exit(1);
76
+ }
77
+
78
+ Ec.waiting(`准备归档需求: ${requirementName}`);
79
+
80
+ try {
81
+ // 获取项目路径
82
+ const projectDir = process.cwd();
83
+ const changesDir = path.join(projectDir, 'specification', 'changes');
84
+ const archivesDir = path.join(projectDir, 'specification', '.archives');
85
+
86
+ // 检查需求是否存在
87
+ if (!_isRequirementExists(changesDir, requirementName)) {
88
+ Ec.error(`❌ 需求 "${requirementName}" 不存在`);
89
+ process.exit(1);
90
+ }
91
+
92
+ // 生成带时间戳的归档目录名 (YYYYMMDDHHMMSS-需求名)
93
+ const now = new Date();
94
+ const timestamp = now.getFullYear() +
95
+ String(now.getMonth() + 1).padStart(2, '0') +
96
+ String(now.getDate()).padStart(2, '0') +
97
+ String(now.getHours()).padStart(2, '0') +
98
+ String(now.getMinutes()).padStart(2, '0') +
99
+ String(now.getSeconds()).padStart(2, '0');
100
+
101
+ const archiveName = `${timestamp}-${requirementName}`;
102
+ const archiveDir = path.join(archivesDir, archiveName);
103
+
104
+ // 确保归档目录存在
105
+ if (!fs.existsSync(archivesDir)) {
106
+ await fsAsync.mkdir(archivesDir, { recursive: true });
107
+ }
108
+
109
+ // 拷贝需求目录到归档目录
110
+ const sourceDir = path.join(changesDir, requirementName);
111
+ Ec.waiting(`正在归档需求 "${requirementName}" 到 "${archiveDir}"`);
112
+ await _copyDir(sourceDir, archiveDir);
113
+
114
+ // 删除原始需求目录
115
+ Ec.waiting(`正在删除原始需求目录 "${sourceDir}"`);
116
+ await _removeDir(sourceDir);
117
+
118
+ Ec.info(`✅ 成功归档需求 "${requirementName}" 为 "${archiveName}"`);
119
+ process.exit(0);
120
+ } catch (error) {
121
+ Ec.error(`❌ 执行过程中发生错误: ${error.message}`);
122
+ process.exit(1);
123
+ }
124
+ };
@@ -0,0 +1,158 @@
1
+ const {exec} = require('child_process');
2
+ const util = require('util');
3
+ const Ec = require('../epic');
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+
7
+ // 将 exec 转换为 Promise 版本
8
+ const execAsync = util.promisify(exec);
9
+
10
+ /**
11
+ * 获取项目版本号
12
+ * @returns {string} 版本号
13
+ */
14
+ const _getVersion = () => {
15
+ try {
16
+ const packagePath = path.resolve(__dirname, '../../package.json');
17
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
18
+ return packageJson.version || '未知版本';
19
+ } catch (error) {
20
+ return '未知版本';
21
+ }
22
+ };
23
+
24
+ /**
25
+ * 检查命令是否存在
26
+ * @param {string} command 命令名称
27
+ * @returns {Promise<boolean>} 命令是否存在
28
+ */
29
+ const _checkCommand = async (command) => {
30
+ try {
31
+ await execAsync(`which ${command}`);
32
+ return true;
33
+ } catch (error) {
34
+ try {
35
+ // 在 Windows 上尝试 where 命令
36
+ await execAsync(`where ${command}`);
37
+ return true;
38
+ } catch (winError) {
39
+ return false;
40
+ }
41
+ }
42
+ };
43
+
44
+ /**
45
+ * 获取命令版本信息
46
+ * @param {string} command 命令名称
47
+ * @returns {Promise<string>} 命令版本信息
48
+ */
49
+ const _getCommandVersion = async (command) => {
50
+ try {
51
+ const {stdout} = await execAsync(`${command} --version`);
52
+ return stdout.trim();
53
+ } catch (error) {
54
+ try {
55
+ // 尝试使用 -v 参数
56
+ const {stdout} = await execAsync(`${command} -v`);
57
+ return stdout.trim();
58
+ } catch (vError) {
59
+ return '未知版本';
60
+ }
61
+ }
62
+ };
63
+
64
+ module.exports = async () => {
65
+ Ec.waiting('正在检查环境信息...');
66
+
67
+ // 需要检查的命令列表
68
+ const requiredCommands = ['node', 'npm', 'git', 'momo'];
69
+
70
+ let allCommandsAvailable = true;
71
+
72
+ // 检查每个命令
73
+ for (const command of requiredCommands) {
74
+ const isAvailable = await _checkCommand(command);
75
+
76
+ if (isAvailable) {
77
+ // const version = await _getCommandVersion(command);
78
+ // Ec.waiting(`✅ ${command}: ${version}`);
79
+ } else {
80
+ Ec.waiting(`❌ ${command}: 未找到命令`);
81
+ allCommandsAvailable = false;
82
+ }
83
+ }
84
+
85
+ Ec.waiting('------------------------------------------------------------');
86
+
87
+ // 检查额外的环境信息
88
+ try {
89
+ const {stdout: nodeVersion} = await execAsync('node --version');
90
+ Ec.waiting(`💻 Node.js 版本: ${nodeVersion.trim()}`);
91
+ } catch (error) {
92
+ Ec.waiting('⚠️ 无法获取 Node.js 版本信息');
93
+ }
94
+
95
+ try {
96
+ const {stdout: npmVersion} = await execAsync('npm --version');
97
+ Ec.waiting(`📦 NPM 版本: ${npmVersion.trim()}`);
98
+ } catch (error) {
99
+ Ec.waiting('⚠️ 无法获取 NPM 版本信息');
100
+ }
101
+
102
+ try {
103
+ const {stdout: gitVersion} = await execAsync('git --version');
104
+ Ec.waiting(`🌱 Git 版本: ${gitVersion.trim()}`);
105
+ } catch (error) {
106
+ Ec.waiting('⚠️ 无法获取 Git 版本信息');
107
+ }
108
+
109
+ // 检查 trae 版本(可选命令)
110
+ try {
111
+ const {stdout: traeVersion} = await execAsync('trae --version');
112
+ const traeItems = (traeVersion.split("\n"));
113
+ Ec.waiting(`----> 🎯 Trae 版本: ${traeItems[0].trim()}`);
114
+ } catch (error) {
115
+ Ec.waiting('⚠️ Trae 未安装或无法获取版本信息(可选工具)');
116
+ }
117
+
118
+ // 检查 lingma 版本(可选命令)
119
+ try {
120
+ const {stdout: lingmaVersion} = await execAsync('lingma --version');
121
+ const lingmaItems = (lingmaVersion.split("\n"));
122
+ Ec.waiting(`----> 🧠 Lingma 版本: ${lingmaItems[0].trim()}`);
123
+ } catch (error) {
124
+ Ec.waiting('⚠️ Lingma 未安装或无法获取版本信息(可选工具)');
125
+ }
126
+
127
+ // 检查 cursor 版本(可选命令)
128
+ try {
129
+ const {stdout: cursorVersion} = await execAsync('cursor --version');
130
+ const cursorItems = (cursorVersion.split("\n"));
131
+ Ec.waiting(`----> 🖱️ Cursor 版本: ${cursorItems[0].trim()}`);
132
+ } catch (error) {
133
+ Ec.waiting('⚠️ Cursor 未安装或无法获取版本信息(可选工具)');
134
+ }
135
+
136
+ // 检查 kiro 版本(可选命令)
137
+ try {
138
+ const {stdout: kiroVersion} = await execAsync('kiro --version');
139
+ const kiroItems = (kiroVersion.split("\n"));
140
+ Ec.waiting(`----> ⚡ Kiro 版本: ${kiroItems[0].trim()}`);
141
+ } catch (error) {
142
+ Ec.waiting('⚠️ Kiro 未安装或无法获取版本信息(可选工具)');
143
+ }
144
+
145
+ // 显示 Momo 版本
146
+ const momoVersion = _getVersion();
147
+ Ec.waiting(`🤖 Momo 版本: ${momoVersion}`);
148
+
149
+ // 总结(只检查必需命令)
150
+ if (allCommandsAvailable) {
151
+ Ec.info('✅ 所有必需的命令都已安装');
152
+ } else {
153
+ Ec.waiting('❌ 部分必需的命令未安装,请检查并安装缺失的命令');
154
+ process.exit(1);
155
+ }
156
+
157
+ process.exit(0);
158
+ };
@@ -0,0 +1,23 @@
1
+ const Ec = require('../epic');
2
+ const os = require('os');
3
+ module.exports = (options) => {
4
+ const metadata = Ec.parseMetadata();
5
+ metadata.forEach(runner => {
6
+ Ec.waiting(`命令:` + `momo ${runner.command} [-options]`.green);
7
+ Ec.waiting(`说明:${runner.description}`.yellow);
8
+ const options = runner.options || [];
9
+ options.forEach(option => {
10
+ if (option.hasOwnProperty('default')) {
11
+ const str = option.default + '';
12
+ Ec.waiting(`\t-${option.alias},--${option.name}`.red + `\t${option.description}, 默认值:\uD83C\uDF37 ` + str.blue);
13
+ } else {
14
+ Ec.waiting(`\t-${option.alias},--${option.name}`.red + `\t${option.description}`);
15
+ }
16
+ })
17
+ Ec.waiting();
18
+ });
19
+ Ec.info("命令执行完成!");
20
+
21
+ // 退出程序
22
+ process.exit(0);
23
+ }
@@ -0,0 +1,321 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const Ec = require('../epic');
4
+ const fsAsync = require("fs").promises;
5
+
6
+ const _ioDirectory = async (baseDir) => {
7
+ 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
+ ];
31
+
32
+ // 创建所有目录
33
+ const results = [];
34
+ for (const folder of folders) {
35
+ Ec.waiting("创建目录:" + folder);
36
+ const directory = path.resolve(baseDir, folder);
37
+ await fsAsync.mkdir(directory, {recursive: true});
38
+ results.push(true);
39
+ }
40
+ return results;
41
+ }
42
+
43
+ const _ioFile = async (baseDir) => {
44
+ /*
45
+ * 填充空文件目录
46
+ * .momo/prompt/
47
+ * .momo/template/ 存放模板目录
48
+ * .momo/advanced/
49
+ * .momo/scripts/
50
+ *
51
+ * specification/changes/
52
+ * specification/actor/
53
+ * specification/.archives/
54
+ * specification/.activities/
55
+ *
56
+ * integration/ 下所有类型的子目录
57
+ */
58
+
59
+ /*
60
+ * 拷贝文件目录
61
+ * _template/specification/project.md
62
+ * _template/specification/project-model.md
63
+ * _template/specification/requirement.md
64
+ */
65
+
66
+ // 检查是否存在任何已存在的文件
67
+ let hasExistingFiles = false;
68
+ const templateDir = path.resolve(__dirname, "../_template/LAIN");
69
+
70
+ // 检查模板文件
71
+ const templateFiles = [
72
+ {
73
+ source: "specification/project.md",
74
+ target: "specification/project.md"
75
+ },
76
+ {
77
+ source: "specification/project-model.md",
78
+ target: "specification/project-model.md"
79
+ },
80
+ {
81
+ source: "specification/requirement.md",
82
+ target: "specification/requirement.md"
83
+ },
84
+ {
85
+ source: ".gitignore",
86
+ target: ".gitignore"
87
+ },
88
+ {
89
+ source: "AGENTS.md",
90
+ target: "AGENTS.md"
91
+ }
92
+ ];
93
+
94
+ for (const file of templateFiles) {
95
+ const sourcePath = path.resolve(templateDir, file.source);
96
+ const targetPath = path.resolve(baseDir, file.target);
97
+
98
+ // 检查源文件是否存在且目标文件已存在
99
+ if (fs.existsSync(sourcePath) && fs.existsSync(targetPath)) {
100
+ hasExistingFiles = true;
101
+ break;
102
+ }
103
+ }
104
+
105
+ // 检查 actor.md 文件
106
+ const actorSourcePath = path.resolve(templateDir, ".momo/advanced/actor.md");
107
+ const actorTargetPath = path.resolve(baseDir, ".momo/advanced/actor.md");
108
+
109
+ if (fs.existsSync(actorSourcePath) && fs.existsSync(actorTargetPath)) {
110
+ hasExistingFiles = true;
111
+ }
112
+
113
+ // 如果有已存在的文件,询问用户是否覆盖
114
+ let shouldOverwrite = true;
115
+ if (hasExistingFiles) {
116
+ const answer = await Ec.ask("检测到已存在文件,是否全部覆盖?(y/N): ");
117
+ shouldOverwrite = (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
118
+ if (!shouldOverwrite) {
119
+ Ec.waiting("跳过所有已存在文件的覆盖操作");
120
+ }
121
+ }
122
+
123
+ // 创建空文件的目录列表
124
+ const emptyFileDirs = [
125
+ ".momo/prompt/",
126
+ ".momo/template/",
127
+ ".momo/scripts/",
128
+ "specification/changes/",
129
+ "specification/actor/",
130
+ "specification/.archives/",
131
+ "specification/.activities/",
132
+ "source/",
133
+ "integration/openspec/",
134
+ "integration/spec-kit/",
135
+ "integration/kiro/",
136
+ "integration/trea/",
137
+ "integration/cursor/",
138
+ "integration/lingma/",
139
+ "integration/qoder/",
140
+ "integration/windsurf/",
141
+ "integration/github/",
142
+ "integration/claude-code/",
143
+ "integration/chatgpt/"
144
+ ];
145
+
146
+ // 为每个目录创建一个空的 .gitkeep 文件,确保目录被git跟踪
147
+ for (const dir of emptyFileDirs) {
148
+ const fullPath = path.resolve(baseDir, dir, ".gitkeep");
149
+ // 确保目录存在后再创建文件
150
+ const dirPath = path.dirname(fullPath);
151
+ if (!fs.existsSync(dirPath)) {
152
+ await fsAsync.mkdir(dirPath, {recursive: true});
153
+ }
154
+
155
+ if (!fs.existsSync(fullPath)) {
156
+ Ec.waiting("创建空文件:" + fullPath);
157
+ await fsAsync.writeFile(fullPath, "");
158
+ }
159
+ }
160
+
161
+ // 拷贝模板文件
162
+ for (const file of templateFiles) {
163
+ const sourcePath = path.resolve(templateDir, file.source);
164
+ const targetPath = path.resolve(baseDir, file.target);
165
+
166
+ // 检查源文件是否存在
167
+ if (fs.existsSync(sourcePath)) {
168
+ // 确保目标目录存在
169
+ const targetDir = path.dirname(targetPath);
170
+ if (!fs.existsSync(targetDir)) {
171
+ await fsAsync.mkdir(targetDir, {recursive: true});
172
+ }
173
+
174
+ // 检查目标文件是否已存在
175
+ if (fs.existsSync(targetPath)) {
176
+ // 目标文件已存在,根据用户选择决定是否覆盖
177
+ if (shouldOverwrite) {
178
+ Ec.waiting("覆盖模板文件:" + file.target);
179
+ await fsAsync.copyFile(sourcePath, targetPath);
180
+ } else {
181
+ Ec.waiting("跳过文件:" + file.target);
182
+ }
183
+ } else {
184
+ Ec.waiting("拷贝模板文件:" + file.target);
185
+ await fsAsync.copyFile(sourcePath, targetPath);
186
+ }
187
+ } else {
188
+ // 如果模板文件不存在,则创建空文件
189
+ if (!fs.existsSync(targetPath)) {
190
+ Ec.waiting("创建空文件:" + targetPath);
191
+ // 确保目标目录存在
192
+ const targetDir = path.dirname(targetPath);
193
+ if (!fs.existsSync(targetDir)) {
194
+ await fsAsync.mkdir(targetDir, {recursive: true});
195
+ }
196
+ await fsAsync.writeFile(targetPath, "");
197
+ }
198
+ }
199
+ }
200
+
201
+ // 拷贝 .momo/advanced/actor.md 文件
202
+ if (fs.existsSync(actorSourcePath)) {
203
+ // 确保目标目录存在
204
+ const actorDir = path.dirname(actorTargetPath);
205
+ if (!fs.existsSync(actorDir)) {
206
+ await fsAsync.mkdir(actorDir, {recursive: true});
207
+ }
208
+
209
+ // 检查目标文件是否已存在
210
+ if (fs.existsSync(actorTargetPath)) {
211
+ // 目标文件已存在,根据用户选择决定是否覆盖
212
+ if (shouldOverwrite) {
213
+ Ec.waiting("覆盖模板文件:" + ".momo/advanced/actor.md");
214
+ await fsAsync.copyFile(actorSourcePath, actorTargetPath);
215
+ } else {
216
+ Ec.waiting("跳过文件:.momo/advanced/actor.md");
217
+ }
218
+ } else {
219
+ Ec.waiting("拷贝模板文件:" + ".momo/advanced/actor.md");
220
+ await fsAsync.copyFile(actorSourcePath, actorTargetPath);
221
+ }
222
+ } else {
223
+ // 如果模板文件不存在,则创建空文件
224
+ if (!fs.existsSync(actorTargetPath)) {
225
+ Ec.waiting("创建文件:" + actorTargetPath);
226
+ // 确保目标目录存在
227
+ const actorDir = path.dirname(actorTargetPath);
228
+ if (!fs.existsSync(actorDir)) {
229
+ await fsAsync.mkdir(actorDir, {recursive: true});
230
+ }
231
+
232
+ await fsAsync.writeFile(actorTargetPath, "");
233
+ }
234
+ }
235
+
236
+ // 确保 .momo/advanced/ 目录存在
237
+ const momoAdvancedDir = path.resolve(baseDir, ".momo/advanced/");
238
+ if (!fs.existsSync(momoAdvancedDir)) {
239
+ await fsAsync.mkdir(momoAdvancedDir, {recursive: true});
240
+ }
241
+ }
242
+
243
+ /**
244
+ * 目录结构说明
245
+ * .momo/prompt/ 存放备注目录
246
+ * .momo/template/ 存放模板目录
247
+ * .momo/advanced/ 高级配置说明
248
+ * /actor.md - 角色定制说明
249
+ * .momo/scripts/ 特殊脚本目录
250
+ *
251
+ * specification/ 工作目录
252
+ * /project.md - 项目基本说明文件
253
+ * /project-model.md - 模型说明文件(通常是建模所需的核心概念模型)
254
+ *
255
+ * --------- 需求分析 / 任务分配设计 / 角色定制 ------------
256
+ * /requirement.md - 基础需求说明(第一份)
257
+ * /changes/ 变更详情
258
+ * /change-XXX/
259
+ * /implementation.md 实现方案步骤说明
260
+ * /proposal.md ( OpenSpec ) 变更提案说明文件
261
+ * /tasks.md ( OpenSpec ) 变更任务列表说明 -> 链接到 tasks/ 目录下的具体任务清单中
262
+ * /tasks 计划详细内容,如果拆分成 Task 则对应到 Tasks/plan-XXX/ 目录中
263
+ * /M01-T001-01.md 具体任务文件,M代表模块,T代表任务,001代表任务编号,01代表子任务序号
264
+ * /M01-T001-02.md ......
265
+ * /design.md ( OpenSpec ) 变更设计说明文件
266
+ * /change.md ( OpenSpec -> specs/profile/spec.md ) 变更说明文件
267
+ * /actor/ 角色目录 / 定义了 Agent 所具有的角色信息,可自定义角色
268
+ * /{name}/stack.md 角色技能(技术栈)说明文件
269
+ * /{name}/limit.md 角色限制说明文件
270
+ * /.archives/ 归档记录
271
+ * /.activities/
272
+ * /actor@plan-XXX/ 角色在某个计划下的历史执行记录(带时间戳)
273
+ * source/ 源代码目录
274
+ * /develop-01
275
+ * /develop-02
276
+ * /develop-03
277
+ * integration/ 集成目录(特殊集成配置)
278
+ * openspec/ OpenSpec 目录
279
+ * /config.json 配置文件
280
+ * spec-kit/ SpecKit 目录
281
+ * kiro/ Kiro 目录
282
+ * trea/ Trea 目录
283
+ * cursor/ Cursor 目录
284
+ * lingma/ Lingma 目录
285
+ * qoder/ Qoder 目录
286
+ * windsurf/ WindSurf 目录
287
+ * github/ Github Copilot 目录
288
+ * claude-code/ Claude Code 目录
289
+ * chatgpt/ ChatGPT 目录
290
+ * @param options
291
+ */
292
+ module.exports = (options) => {
293
+ // 参数提取
294
+ const parsed = Ec.parseArgument(options);
295
+ /*
296
+ * 基本信息
297
+ */
298
+ const directory = parsed.directory || '.';
299
+ // 创建基础目录结构
300
+ const basePath = path.resolve(process.cwd(), directory);
301
+
302
+ Ec.waiting(`准备在目录 "${basePath}" 中初始化 Spec 工程...`);
303
+
304
+ // 创建目录结构
305
+ _ioDirectory(basePath)
306
+ .then(() => _ioFile(basePath))
307
+ .then(() => {
308
+ Ec.info('✅ SPEC / 项目初始化完成!');
309
+ // 关闭 readline 接口
310
+ Ec.askClose();
311
+ // 退出程序
312
+ process.exit(0);
313
+ })
314
+ .catch((error) => {
315
+ Ec.error('项目初始化失败: ' + error.message);
316
+ // 关闭 readline 接口
317
+ Ec.askClose();
318
+ // 退出程序
319
+ process.exit(1);
320
+ });
321
+ }