momo-ai 1.0.3 → 1.0.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "momo-ai",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Rachel Momo ( OpenSpec )",
5
5
  "main": "src/momo.js",
6
6
  "bin": {
@@ -22,20 +22,28 @@ const outString = (paths, content, sync = false) => __FX.fxContinue(!!content, (
22
22
  const outCopy = (data) => new Promise(function (resolve, reject) {
23
23
  const platform = os.platform();
24
24
  let cmd = '';
25
- if (os.platform() === 'win32') {
25
+ let args = [];
26
+
27
+ if (platform === 'win32') {
26
28
  cmd = 'clip';
27
- } else if (os.platform() === 'darwin') {
29
+ } else if (platform === 'darwin') {
28
30
  cmd = 'pbcopy';
29
31
  } else {
30
- __FX.fxError(10032, platform);
32
+ // 对于Linux和其他Unix-like系统,尝试使用xclip
33
+ cmd = 'xclip';
34
+ args = ['-selection', 'clipboard'];
31
35
  }
32
36
 
33
- const proc = require('child_process').spawn(cmd);
37
+ const proc = require('child_process').spawn(cmd, args);
34
38
  proc.on('error', function (err) {
35
39
  reject(err);
36
40
  });
37
- proc.on('close', function (err) {
38
- resolve();
41
+ proc.on('close', function (code) {
42
+ if (code === 0) {
43
+ resolve();
44
+ } else {
45
+ reject(new Error(`剪贴板进程退出码: ${code}`));
46
+ }
39
47
  });
40
48
  proc.stdin.write(data);
41
49
  proc.stdin.end();
@@ -49,65 +49,85 @@ const _selectLLM = async (actorName) => {
49
49
  };
50
50
  };
51
51
 
52
- module.exports = (options) => {
52
+ module.exports = async (options) => {
53
53
  // 参数提取
54
54
  const parsed = Ec.parseArgument(options);
55
+ const actorName = parsed.actor || parsed.a;
55
56
 
56
- const actorName = parsed.name;
57
- Ec.waiting(`🔍 准备创建角色: ${actorName}`);
58
-
59
- // 创建角色目录
60
- const actorDir = path.resolve(process.cwd(), 'specification', 'actor', actorName);
61
- if (!fs.existsSync(actorDir)) {
62
- Ec.waiting(`📁 创建角色目录: ${actorDir}`);
63
- fs.mkdirSync(actorDir, {recursive: true});
64
- } else {
65
- Ec.waiting(`✅ 角色目录已存在: ${actorDir}`);
57
+ if (!actorName) {
58
+ Ec.error("❌ 请提供角色名称 (-a, --actor)");
59
+ process.exit(1);
60
+ }
61
+
62
+ // Windows 上检查非法字符
63
+ if (process.platform === 'win32') {
64
+ const illegalChars = /[<>:"/\\|?*\x00-\x1F]/g;
65
+ if (illegalChars.test(actorName)) {
66
+ Ec.error(`❌ 角色名称 "${actorName}" 包含非法字符,请避免使用以下字符: < > : " / \\ | ? * 以及控制字符`);
67
+ process.exit(1);
68
+ }
66
69
  }
67
70
 
68
- // 从模板复制内容到stack.md和limit.md
69
- const templatePath = path.resolve(__dirname, '../_template/LAIN/.momo/advanced/actor.md');
70
- if (fs.existsSync(templatePath)) {
71
- const templateContent = fs.readFileSync(templatePath, 'utf8');
72
-
73
- // 提取技能说明部分作为stack.md的内容
74
- const stackMatch = templateContent.match(/## 技能说明\s*([\s\S]*?)\s*## 角色限制/);
75
- if (stackMatch && stackMatch[1]) {
76
- const stackContent = `# 技术栈信息\n\n${stackMatch[1].trim()}\n`;
77
- const stackPath = path.resolve(actorDir, 'stack.md');
78
- fs.writeFileSync(stackPath, stackContent);
79
- Ec.waiting(`📄 创建技术栈文件: ${stackPath}`);
71
+ try {
72
+ // 检查 specification/actor 目录是否存在
73
+ const actorBaseDir = path.resolve(process.cwd(), 'specification', 'actor');
74
+ if (!fs.existsSync(actorBaseDir)) {
75
+ Ec.error("❌ 未找到 specification/actor 目录,请先初始化项目");
76
+ process.exit(1);
80
77
  }
81
78
 
82
- // 提取角色限制部分作为limit.md的内容
83
- const limitMatch = templateContent.match(/## 角色限制\s*([\s\S]*)/);
84
- if (limitMatch && limitMatch[1]) {
85
- const limitContent = `# 角色限制\n\n${limitMatch[1].trim()}\n`;
86
- const limitPath = path.resolve(actorDir, 'limit.md');
87
- fs.writeFileSync(limitPath, limitContent);
88
- Ec.waiting(`📄 创建限制文件: ${limitPath}`);
79
+ // 检查角色是否已存在
80
+ const actorDir = path.resolve(actorBaseDir, actorName);
81
+ if (fs.existsSync(actorDir)) {
82
+ Ec.error(`❌ 角色 "${actorName}" 已存在`);
83
+ process.exit(1);
89
84
  }
90
- }
91
85
 
92
- // 交互式选择LLM
93
- _selectLLM(actorName)
94
- .then((llmConfig) => {
95
- // 创建config.json文件
96
- const configPath = path.resolve(actorDir, 'config.json');
97
- fs.writeFileSync(configPath, JSON.stringify(llmConfig, null, 4));
98
- Ec.waiting(`⚙️ 创建配置文件: ${configPath}`);
86
+ // 创建角色目录
87
+ if (!fs.existsSync(actorDir)) {
88
+ fs.mkdirSync(actorDir, { recursive: true });
89
+ }
99
90
 
100
- Ec.info(`✅ 角色 "${actorName}" 创建完成`);
101
- // 关闭 readline 接口
102
- Ec.askClose();
103
- // 退出程序
104
- process.exit(0);
105
- })
106
- .catch((error) => {
107
- Ec.error(`❌ 执行过程中发生错误: ${error.message}`);
108
- // 关闭 readline 接口
109
- Ec.askClose();
110
- // 退出程序
91
+ // 从模板复制内容到stack.md和limit.md
92
+ const templatePath = path.resolve(__dirname, '../_template/LAIN/.momo/advanced/actor.md');
93
+ if (fs.existsSync(templatePath)) {
94
+ const templateContent = fs.readFileSync(templatePath, 'utf8');
95
+
96
+ // 提取技术栈和限制信息
97
+ const stackMatch = templateContent.match(/## 技术栈([\s\S]*?)## 限制/);
98
+ const limitMatch = templateContent.match(/## 限制([\s\S]*)/);
99
+
100
+ if (stackMatch && limitMatch) {
101
+ const stackContent = `# 技术栈信息\n\n${stackMatch[1].trim()}\n`;
102
+ const stackPath = path.resolve(actorDir, 'stack.md');
103
+ fs.writeFileSync(stackPath, stackContent);
104
+ Ec.waiting(`📄 创建技术栈文件: ${stackPath}`);
105
+
106
+ const limitContent = `# 角色限制\n\n${limitMatch[1].trim()}\n`;
107
+ const limitPath = path.resolve(actorDir, 'limit.md');
108
+ fs.writeFileSync(limitPath, limitContent);
109
+ Ec.waiting(`📄 创建限制文件: ${limitPath}`);
110
+
111
+ // 创建config.json文件
112
+ const configPath = path.resolve(actorDir, 'config.json');
113
+ fs.writeFileSync(configPath, JSON.stringify(llmConfig, null, 4));
114
+ Ec.waiting(`📄 创建配置文件: ${configPath}`);
115
+
116
+ Ec.info(`✅ 成功添加新角色 "${actorName}"`);
117
+ process.exit(0);
118
+ } else {
119
+ Ec.error("❌ 模板文件格式不正确");
120
+ process.exit(1);
121
+ }
122
+ } else {
123
+ Ec.error(`❌ 未找到模板文件: ${templatePath}`);
111
124
  process.exit(1);
112
- });
113
- };
125
+ }
126
+ } catch (error) {
127
+ Ec.error(`❌ 执行过程中发生错误: ${error.message}`);
128
+ if (process.platform === 'win32') {
129
+ Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
130
+ }
131
+ process.exit(1);
132
+ }
133
+ };
@@ -1,9 +1,9 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const util = require('util');
4
- const { spawn } = require('child_process');
5
4
  const Ec = require('../epic');
6
5
  const fsAsync = require('fs').promises;
6
+ const { outCopy } = require('../epic/momo.fn.out');
7
7
 
8
8
  /**
9
9
  * 读取模板文件并提取 <!-- BEGIN --> 到 <!-- END --> 之间的内容
@@ -34,6 +34,9 @@ const _readTemplate = async (templatePath) => {
34
34
  return content;
35
35
  } catch (error) {
36
36
  Ec.waiting(`读取模板文件失败: ${error.message}`);
37
+ if (process.platform === 'win32') {
38
+ Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
39
+ }
37
40
  throw error;
38
41
  }
39
42
  };
@@ -117,6 +120,10 @@ const _copyTemplateFiles = async (templateDir, targetDir, sourceFilePath = null)
117
120
  } else if (fs.existsSync(proposalSource)) {
118
121
  Ec.waiting(`拷贝文件: ${proposalTarget}`);
119
122
  await fsAsync.copyFile(proposalSource, proposalTarget);
123
+ } else {
124
+ // 如果模板文件不存在,则创建空文件
125
+ Ec.waiting(`创建空文件: ${proposalTarget}`);
126
+ await fsAsync.writeFile(proposalTarget, '');
120
127
  }
121
128
 
122
129
  // 拷贝 tasks.md
@@ -125,6 +132,10 @@ const _copyTemplateFiles = async (templateDir, targetDir, sourceFilePath = null)
125
132
  if (fs.existsSync(tasksSource)) {
126
133
  Ec.waiting(`拷贝文件: ${tasksTarget}`);
127
134
  await fsAsync.copyFile(tasksSource, tasksTarget);
135
+ } else {
136
+ // 如果模板文件不存在,则创建空文件
137
+ Ec.waiting(`创建空文件: ${tasksTarget}`);
138
+ await fsAsync.writeFile(tasksTarget, '');
128
139
  }
129
140
 
130
141
  // 创建 tasks 目录
@@ -140,9 +151,16 @@ const _copyTemplateFiles = async (templateDir, targetDir, sourceFilePath = null)
140
151
  if (fs.existsSync(taskDetailSource)) {
141
152
  Ec.waiting(`拷贝文件: ${taskDetailTarget}`);
142
153
  await fsAsync.copyFile(taskDetailSource, taskDetailTarget);
154
+ } else {
155
+ // 如果模板文件不存在,则创建空文件
156
+ Ec.waiting(`创建空文件: ${taskDetailTarget}`);
157
+ await fsAsync.writeFile(taskDetailTarget, '');
143
158
  }
144
159
  } catch (error) {
145
160
  Ec.waiting(`拷贝模板文件失败: ${error.message}`);
161
+ if (process.platform === 'win32') {
162
+ Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
163
+ }
146
164
  throw error;
147
165
  }
148
166
  };
@@ -155,15 +173,18 @@ const _copyToClipboard = async (content) => {
155
173
  try {
156
174
  // 去除换行符,将内容合并为一行
157
175
  const contentWithoutNewlines = content.replace(/\r?\n|\r/g, ' ');
158
- const proc = spawn('pbcopy', { stdio: 'pipe' });
159
- proc.stdin.write(contentWithoutNewlines);
160
- proc.stdin.end();
176
+
177
+ // 使用统一的剪贴板函数
178
+ await outCopy(contentWithoutNewlines);
161
179
  Ec.waiting('✅ 提示词已复制到剪贴板');
162
180
  } catch (error) {
163
181
  Ec.waiting(`复制到剪贴板失败: ${error.message}`);
164
- // 在非 macOS 系统上可能没有 pbcopy,提供备选方案
182
+ // 提供备选方案
165
183
  Ec.waiting('提示词内容:');
166
184
  console.log(content);
185
+ if (process.platform === 'win32') {
186
+ Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
187
+ }
167
188
  }
168
189
  };
169
190
 
@@ -203,6 +224,15 @@ module.exports = async (options) => {
203
224
  requirementName = await _extractTitleFromMarkdown(sourceFilePath);
204
225
  }
205
226
 
227
+ // 检查需求名称是否包含非法字符(Windows 上特别检查)
228
+ if (process.platform === 'win32') {
229
+ const illegalChars = /[<>:"/\\|?*\x00-\x1F]/g;
230
+ if (illegalChars.test(requirementName)) {
231
+ Ec.error(`❌ 需求名称 "${requirementName}" 包含非法字符,请避免使用以下字符: < > : " / \\ | ? * 以及控制字符`);
232
+ process.exit(1);
233
+ }
234
+ }
235
+
206
236
  // 检查需求名称是否包含点号(除了允许的独立需求)
207
237
  if (!hasExtension && requirementName.includes('.')) {
208
238
  Ec.error("❌ 独立需求名称不能包含点号(.),以防止与文件扩展名混淆");
@@ -224,10 +254,36 @@ module.exports = async (options) => {
224
254
  process.exit(1);
225
255
  }
226
256
 
257
+ // 确保 changes 目录存在
258
+ if (!fs.existsSync(changesDir)) {
259
+ Ec.waiting(`创建目录: ${changesDir}`);
260
+ try {
261
+ await fsAsync.mkdir(changesDir, { recursive: true });
262
+ } catch (mkdirError) {
263
+ Ec.error(`❌ 创建目录失败: ${mkdirError.message}`);
264
+ if (process.platform === 'win32') {
265
+ Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
266
+ }
267
+ process.exit(1);
268
+ }
269
+ }
270
+
227
271
  // 创建需求目录
228
272
  const requirementDir = path.join(changesDir, requirementName);
229
273
  Ec.waiting(`创建需求目录: ${requirementDir}`);
230
- await fsAsync.mkdir(requirementDir, { recursive: true });
274
+ try {
275
+ await fsAsync.mkdir(requirementDir, { recursive: true });
276
+ } catch (mkdirError) {
277
+ Ec.error(`❌ 创建目录失败: ${mkdirError.message}`);
278
+ if (process.platform === 'win32') {
279
+ Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
280
+ // 检查是否是由于特殊字符导致的问题
281
+ if (mkdirError.message.includes('Invalid argument') || mkdirError.message.includes('EINVAL')) {
282
+ Ec.waiting('💡 可能是由于需求名称包含非法字符,请尝试使用更简单的名称');
283
+ }
284
+ }
285
+ process.exit(1);
286
+ }
231
287
 
232
288
  // 拷贝模板文件
233
289
  await _copyTemplateFiles(templateDir, requirementDir, sourceFilePath);
@@ -243,6 +299,9 @@ module.exports = async (options) => {
243
299
  process.exit(0);
244
300
  } catch (error) {
245
301
  Ec.error(`❌ 执行过程中发生错误: ${error.message}`);
302
+ if (process.platform === 'win32') {
303
+ Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
304
+ }
246
305
  process.exit(1);
247
306
  }
248
307
  };
@@ -22,22 +22,18 @@ const _getVersion = () => {
22
22
  };
23
23
 
24
24
  /**
25
- * 检查命令是否存在
25
+ * 检查命令是否可用
26
26
  * @param {string} command 命令名称
27
- * @returns {Promise<boolean>} 命令是否存在
27
+ * @returns {Promise<boolean>} 命令是否可用
28
28
  */
29
- const _checkCommand = async (command) => {
29
+ const _isCommandAvailable = async (command) => {
30
30
  try {
31
- await execAsync(`which ${command}`);
31
+ // 在 Windows 上使用 where 命令,在其他系统上使用 which 命令
32
+ const whereCmd = process.platform === 'win32' ? 'where' : 'which';
33
+ await execAsync(`${whereCmd} ${command}`);
32
34
  return true;
33
35
  } catch (error) {
34
- try {
35
- // 在 Windows 上尝试 where 命令
36
- await execAsync(`where ${command}`);
37
- return true;
38
- } catch (winError) {
39
- return false;
40
- }
36
+ return false;
41
37
  }
42
38
  };
43
39
 
@@ -71,7 +67,7 @@ module.exports = async () => {
71
67
 
72
68
  // 检查每个命令
73
69
  for (const command of requiredCommands) {
74
- const isAvailable = await _checkCommand(command);
70
+ const isAvailable = await _isCommandAvailable(command);
75
71
 
76
72
  if (isAvailable) {
77
73
  // const version = await _getCommandVersion(command);
@@ -35,8 +35,15 @@ const _ioDirectory = async (baseDir) => {
35
35
  for (const folder of folders) {
36
36
  Ec.waiting("创建目录:" + folder);
37
37
  const directory = path.resolve(baseDir, folder);
38
- await fsAsync.mkdir(directory, {recursive: true});
39
- results.push(true);
38
+ try {
39
+ await fsAsync.mkdir(directory, {recursive: true});
40
+ results.push(true);
41
+ } catch (error) {
42
+ if (process.platform === 'win32') {
43
+ Ec.waiting(`💡 Windows 用户提示: 创建目录失败,可能是由于权限不足或路径包含非法字符`);
44
+ }
45
+ throw error;
46
+ }
40
47
  }
41
48
  return results;
42
49
  }
@@ -96,6 +96,9 @@ module.exports = async (options) => {
96
96
 
97
97
  } catch (error) {
98
98
  Ec.error(`❌ 执行过程中发生错误: ${error.message}`);
99
+ if (process.platform === 'win32') {
100
+ Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
101
+ }
99
102
  Ec.askClose();
100
103
  process.exit(1);
101
104
  }
@@ -108,7 +111,9 @@ module.exports = async (options) => {
108
111
  */
109
112
  const _isCommandAvailable = async (command) => {
110
113
  return new Promise((resolve) => {
111
- const process = spawn('which', [command]);
114
+ // Windows 上使用 where 命令,在其他系统上使用 which 命令
115
+ const whereCmd = process.platform === 'win32' ? 'where' : 'which';
116
+ const process = spawn(whereCmd, [command]);
112
117
  process.on('close', (code) => {
113
118
  resolve(code === 0);
114
119
  });
@@ -140,6 +145,9 @@ const _openWithTool = async (tool) => {
140
145
  child.on('error', (error) => {
141
146
  if (error.code === 'ENOENT') {
142
147
  Ec.error(`❌ 未找到命令: ${tool},请确保已正确安装`);
148
+ if (process.platform === 'win32') {
149
+ Ec.waiting('💡 Windows 用户提示: 请确保工具已正确安装并在 PATH 环境变量中');
150
+ }
143
151
  reject(new Error(`未找到命令: ${tool},请确保已正确安装`));
144
152
  } else {
145
153
  Ec.error(`❌ 执行 ${tool} 时发生错误: ${error.message}`);
@@ -1,8 +1,8 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
- const { spawn } = require('child_process');
4
3
  const Ec = require('../epic');
5
4
  const fsAsync = require('fs').promises;
5
+ const { outCopy } = require('../epic/momo.fn.out');
6
6
 
7
7
  /**
8
8
  * 读取模板文件并提取 <!-- BEGIN --> 到 <!-- END --> 之间的内容
@@ -72,11 +72,11 @@ const _isRequirementExists = (changesDir, requirementName) => {
72
72
  */
73
73
  const _copyToClipboard = async (content, requirementName) => {
74
74
  try {
75
- // 去除换行符,将内容合并为一行后再复制到剪贴板
75
+ // 去除换行符,将内容合并为一行
76
76
  const contentWithoutNewlines = content.replace(/\r?\n|\r/g, ' ');
77
- const proc = spawn('pbcopy', { stdio: 'pipe' });
78
- proc.stdin.write(contentWithoutNewlines);
79
- proc.stdin.end();
77
+
78
+ // 使用统一的剪贴板函数
79
+ await outCopy(contentWithoutNewlines);
80
80
  Ec.waiting('✅ 计划提示词已复制到剪贴板');
81
81
 
82
82
  // 保存内容到 .working 目录
@@ -94,9 +94,12 @@ const _copyToClipboard = async (content, requirementName) => {
94
94
  Ec.waiting(`📄 计划提示词已保存到文件: ${filePath}`);
95
95
  } catch (error) {
96
96
  Ec.waiting(`复制到剪贴板失败: ${error.message}`);
97
- // 在非 macOS 系统上可能没有 pbcopy,提供备选方案
97
+ // 提供备选方案
98
98
  Ec.waiting('计划提示词内容:');
99
99
  console.log(content);
100
+ if (process.platform === 'win32') {
101
+ Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
102
+ }
100
103
  }
101
104
  };
102
105
 
@@ -119,6 +122,15 @@ module.exports = async (options) => {
119
122
  process.exit(1);
120
123
  }
121
124
 
125
+ // 在 Windows 上检查非法字符
126
+ if (process.platform === 'win32') {
127
+ const illegalChars = /[<>:"/\\|?*\x00-\x1F]/g;
128
+ if (illegalChars.test(requirementName)) {
129
+ Ec.error(`❌ 需求名称 "${requirementName}" 包含非法字符,请避免使用以下字符: < > : " / \\ | ? * 以及控制字符`);
130
+ process.exit(1);
131
+ }
132
+ }
133
+
122
134
  Ec.waiting(`准备生成需求计划: ${requirementName}`);
123
135
 
124
136
  try {
@@ -144,6 +156,9 @@ module.exports = async (options) => {
144
156
  process.exit(0);
145
157
  } catch (error) {
146
158
  Ec.error(`❌ 执行过程中发生错误: ${error.message}`);
159
+ if (process.platform === 'win32') {
160
+ Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
161
+ }
147
162
  process.exit(1);
148
163
  }
149
164
  };
@@ -26,11 +26,15 @@ const _isGitRepo = async () => {
26
26
  const _initGitRepo = async () => {
27
27
  try {
28
28
  Ec.waiting('正在初始化 Git 仓库');
29
- const command = 'git init -b master';
29
+ // Windows 上使用不同的命令格式
30
+ const command = process.platform === 'win32' ? 'git init' : 'git init -b master';
30
31
  await execAsync(command);
31
32
  Ec.waiting('✅ 成功初始化 Git 仓库');
32
33
  } catch (error) {
33
34
  Ec.error(`❌ 初始化 Git 仓库失败: ${error.message}`);
35
+ if (process.platform === 'win32') {
36
+ Ec.waiting('💡 Windows 用户提示: 请确保已安装 Git 并在 PATH 环境变量中');
37
+ }
34
38
  throw error;
35
39
  }
36
40
  };
@@ -2,7 +2,7 @@ const Ec = require('../epic');
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
4
  const util = require('util');
5
- const {spawn} = require('child_process');
5
+ const { outCopy } = require('../epic/momo.fn.out');
6
6
  const fsAsync = require('fs').promises;
7
7
 
8
8
  /**
@@ -63,22 +63,8 @@ const _renderTemplate = (template, data) => {
63
63
  */
64
64
  const _copyToClipboard = async (content, requirementName, taskName) => {
65
65
  try {
66
- const proc = spawn('pbcopy', {stdio: 'pipe'});
67
- proc.stdin.write(content);
68
- proc.stdin.end();
69
-
70
- // 等待复制操作完成
71
- await new Promise((resolve, reject) => {
72
- proc.on('close', (code) => {
73
- if (code === 0) {
74
- resolve();
75
- } else {
76
- reject(new Error(`pbcopy 进程退出码: ${code}`));
77
- }
78
- });
79
- proc.on('error', reject);
80
- });
81
-
66
+ // 使用统一的剪贴板函数
67
+ await outCopy(content);
82
68
  Ec.waiting('📄 任务执行提示词已拷贝到剪切板');
83
69
 
84
70
  // 保存内容到 .working 目录
@@ -99,6 +85,9 @@ const _copyToClipboard = async (content, requirementName, taskName) => {
99
85
  // 备选方案:显示内容
100
86
  Ec.waiting('提示词内容:');
101
87
  console.log(content);
88
+ if (process.platform === 'win32') {
89
+ Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
90
+ }
102
91
  }
103
92
  };
104
93
 
@@ -1,9 +1,9 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const util = require('util');
4
- const { spawn } = require('child_process');
5
4
  const Ec = require('../epic');
6
5
  const fsAsync = require('fs').promises;
6
+ const { outCopy } = require('../epic/momo.fn.out');
7
7
 
8
8
  /**
9
9
  * 读取模板文件并提取 <!-- BEGIN --> 到 <!-- END --> 之间的内容
@@ -61,28 +61,17 @@ const _renderTemplate = (template, data) => {
61
61
  */
62
62
  const _copyToClipboard = async (content) => {
63
63
  try {
64
- const proc = spawn('pbcopy', { stdio: 'pipe' });
65
- proc.stdin.write(content);
66
- proc.stdin.end();
67
-
68
- // 等待复制操作完成
69
- await new Promise((resolve, reject) => {
70
- proc.on('close', (code) => {
71
- if (code === 0) {
72
- resolve();
73
- } else {
74
- reject(new Error(`pbcopy 进程退出码: ${code}`));
75
- }
76
- });
77
- proc.on('error', reject);
78
- });
79
-
64
+ // 使用统一的剪贴板函数
65
+ await outCopy(content);
80
66
  Ec.waiting('📄 任务检查提示词已拷贝到剪切板');
81
67
  } catch (error) {
82
68
  Ec.waiting(`⚠️ 无法拷贝到剪切板: ${error.message}`);
83
69
  // 备选方案:显示内容
84
70
  Ec.waiting('提示词内容:');
85
71
  console.log(content);
72
+ if (process.platform === 'win32') {
73
+ Ec.waiting('💡 Windows 用户提示: 请确保您在具有足够权限的命令行中运行此命令');
74
+ }
86
75
  }
87
76
  };
88
77