ai-worktool 1.0.18 → 1.0.34

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.
@@ -47,7 +47,7 @@ const setConversationId = (id) => {
47
47
  };
48
48
  exports.setConversationId = setConversationId;
49
49
  async function postData(url, data) {
50
- const token = (0, user_1.getTokenStorage)().get();
50
+ const token = await (0, user_1.getTokenStorage)().get();
51
51
  try {
52
52
  // 发送POST请求
53
53
  const response = await fetch(url, {
@@ -91,26 +91,31 @@ async function handleToolCall(toolCall) {
91
91
  if (!tool) {
92
92
  throw new Error(`Tool ${toolCall.toolName} not found`);
93
93
  }
94
+ console.log(toolCall.toolName, toolCall.params);
94
95
  data = (await (0, toolCall_1.callWithObject)(tool, toolCall.params)) || '执行成功';
95
96
  if (toolCall.toolName === 'readFile') {
96
97
  data = `\n\`\`\`${(0, toolCall_1.inferCodeTypeFromExtension)(toolCall.params.filePath)}\n${data}\n\`\`\``;
97
98
  }
99
+ console.log(toolCall.toolName, data);
98
100
  success = true;
99
101
  }
100
102
  catch (err) {
101
103
  success = false;
102
104
  message = err.message || '执行失败';
105
+ console.error(err);
103
106
  }
104
107
  await toolCallback(toolCall.toolName, toolCall.callId, data || message).catch(console.error);
105
108
  return { data, message, success };
106
109
  }
107
- async function startAgent(name, question, vars, withConversation, ctrl, onMessage, onToolCall) {
110
+ async function startAgent(name, question, vars, withConversation, ctrl, mode, onMessage, onToolCall) {
108
111
  let err = null;
109
112
  let msg = '';
110
113
  let lastMsgNo = 0;
111
114
  const baseURL = process.env.AI_SERVER || DEFAULT_SERVER;
112
- const token = (0, user_1.getTokenStorage)().get();
115
+ const token = await (0, user_1.getTokenStorage)().get();
113
116
  console.log(`Starting agent: ${baseURL}, token: ${token}`);
117
+ console.log(question);
118
+ let lastToolId = null;
114
119
  await (0, fetch_sse_1.fetchEventData)(`${baseURL}/api/agent/exec/aicoder`, {
115
120
  method: 'POST',
116
121
  signal: ctrl?.signal,
@@ -139,22 +144,37 @@ async function startAgent(name, question, vars, withConversation, ctrl, onMessag
139
144
  },
140
145
  async onMessage(ev) {
141
146
  const data = JSON.parse(ev.data);
142
- // console.log(ev?.event, data);
147
+ // console.log('Agent Message:', ev?.event, data);
143
148
  if (ev?.event === 'MESSAGE') {
144
149
  if (data.no !== lastMsgNo) {
145
150
  lastMsgNo = data.no;
146
151
  msg = '';
147
152
  }
148
- msg += data.content;
153
+ if (mode === 'full') {
154
+ msg += data.content;
155
+ }
156
+ else {
157
+ msg = data.content;
158
+ }
149
159
  await onMessage?.(msg, 'msg-' + data.id + data.no);
150
160
  }
161
+ if (ev?.event === 'TOOL' && lastToolId !== 'tool-' + data.id + data.no) {
162
+ lastToolId = 'tool-' + data.id + data.no;
163
+ await onMessage?.((0, toolCall_1.formatToolCallInfo)({
164
+ // eslint-disable-next-line @typescript-eslint/naming-convention
165
+ function_name: data.name,
166
+ params: data.params
167
+ }, vars.projectRoot), lastToolId);
168
+ }
151
169
  if (ev?.event === 'TOOLCALL') {
152
- const id = 'tool-' + data.callId;
170
+ if (!lastToolId) {
171
+ console.warn('TOOLCALL without TOOL!');
172
+ }
153
173
  await onMessage?.((0, toolCall_1.formatToolCallInfo)({
154
174
  // eslint-disable-next-line @typescript-eslint/naming-convention
155
175
  function_name: data.toolName,
156
176
  params: data.params
157
- }, vars.projectRoot), id);
177
+ }, vars.projectRoot), lastToolId);
158
178
  const result = await handleToolCall(data);
159
179
  await onToolCall?.({
160
180
  functionName: data.toolName,
@@ -165,13 +185,13 @@ async function startAgent(name, question, vars, withConversation, ctrl, onMessag
165
185
  // eslint-disable-next-line @typescript-eslint/naming-convention
166
186
  function_name: data.toolName,
167
187
  params: data.params
168
- }, vars.projectRoot, result), id);
188
+ }, vars.projectRoot, result), lastToolId);
169
189
  }
170
190
  if (ev?.event === 'DONE') {
171
191
  console.log(msg);
172
192
  }
173
193
  if (ev?.event === 'ERROR') {
174
- err = data.message;
194
+ err = data.error;
175
195
  }
176
196
  }
177
197
  });
@@ -10,32 +10,32 @@ const path_1 = __importDefault(require("path"));
10
10
  const tools_1 = require("../tools");
11
11
  const MAXLEN = 10000;
12
12
  function fixErrorPrompt(error) {
13
- return `单测执行失败,日志如下:
14
- \`\`\`
15
- ${error.slice(0, MAXLEN)}
16
- \`\`\`
13
+ return `单测执行失败,日志如下:
14
+ \`\`\`
15
+ ${error.slice(0, MAXLEN)}
16
+ \`\`\`
17
17
  `;
18
18
  }
19
19
  // 提示词管理,根据任务合成特定提示词
20
20
  async function fixCodePrompt(failedTest) {
21
- return `测试文件"${path_1.default.resolve(failedTest.testFilePath)}"${failedTest.testName ? '里的"' + failedTest.testName + '"' : ''}执行失败.
22
-
23
- 错误信息为:
24
- \`\`\`
25
- ${failedTest.errorMessages.join('\n')}
26
- \`\`\`
27
-
28
- 测试文件内容如下:
29
- \`\`\`
30
- ${await (0, tools_1.readFile)(failedTest.testFilePath, 'utf8', true)}
21
+ return `测试文件"${path_1.default.resolve(failedTest.testFilePath)}"${failedTest.testName ? '里的"' + failedTest.testName + '"' : ''}执行失败.
22
+
23
+ 错误信息为:
24
+ \`\`\`
25
+ ${failedTest.errorMessages.join('\n')}
26
+ \`\`\`
27
+
28
+ 测试文件内容如下:
29
+ \`\`\`
30
+ ${await (0, tools_1.readFile)(failedTest.testFilePath, 'utf8', true)}
31
31
  \`\`\``;
32
32
  }
33
33
  async function addUncoveredLinePrompt(srcPath, report) {
34
- return `源文件"${path_1.default.resolve(srcPath)}"的单元测试覆盖率是${report.coverage.lines.pct}%, 还有"${report.uncoveredLines.join(',')}"行未覆盖单测。
35
-
36
- 源文件内容如下:
37
- \`\`\`
38
- ${await (0, tools_1.readFile)(srcPath, 'utf8', true)}
34
+ return `源文件"${path_1.default.resolve(srcPath)}"的单元测试覆盖率是${report.coverage.lines.pct}%, 还有"${report.uncoveredLines.join(',')}"行未覆盖单测。
35
+
36
+ 源文件内容如下:
37
+ \`\`\`
38
+ ${await (0, tools_1.readFile)(srcPath, 'utf8', true)}
39
39
  \`\`\``;
40
40
  }
41
41
  //# sourceMappingURL=prompt.js.map
@@ -106,28 +106,33 @@ function getParamNames(fn) {
106
106
  }
107
107
  return paramNames;
108
108
  }
109
+ function formatDisplayText(txt) {
110
+ return txt.length > 50 ? txt.slice(0, 50) + '...' : txt;
111
+ }
109
112
  function formatToolCallInfo(
110
113
  // eslint-disable-next-line @typescript-eslint/naming-convention
111
114
  toolCall, root, result) {
112
115
  root = root || '/';
116
+ const params = toolCall.params || {};
113
117
  if (toolCall.function_name === 'readFile') {
114
118
  const outputResult = result ? (result?.success ? ` ✅` : result?.message || '❌') : '';
115
- return `读取文件:${path_1.default.relative(root, toolCall.params[0] || toolCall.params.filePath)}${outputResult}`;
119
+ const filePath = params[0] || params.filePath;
120
+ return `读取文件:${formatDisplayText(filePath ? path_1.default.relative(root, filePath) : '')}${outputResult}`;
116
121
  }
117
122
  if (toolCall.function_name === 'writeFile') {
118
123
  const outputResult = result ? (result?.success ? ` ✅` : result?.message || '❌') : '';
119
- return `修改文件:${path_1.default.relative(root, toolCall.params[0] || toolCall.params.filePath)}${outputResult}`;
124
+ const filePath = params[0] || params.filePath;
125
+ return `修改文件:${formatDisplayText(filePath ? path_1.default.relative(root, filePath) : '')}${outputResult}`;
120
126
  }
121
127
  if (toolCall.function_name === 'searchFilesByExtension') {
122
128
  const outputResult = result
123
129
  ? result?.success
124
- ? ` => ${result.data
125
- .map((filePath) => path_1.default.relative(root, filePath))
126
- .join(', ')
127
- .slice(0, 50)} ✅`
130
+ ? ` => ${formatDisplayText(result.data.map((filePath) => path_1.default.relative(root, filePath)).join(', '))} ✅`
128
131
  : result?.message || '❌'
129
132
  : '';
130
- return `检索文件:${path_1.default.relative(root, toolCall.params[0] || toolCall.params.directory)}/*${toolCall.params[1] || toolCall.params.extensions}${outputResult}`;
133
+ const exts = params[1] || params.extensions;
134
+ const directory = path_1.default.relative(root, params[0] || params.directory);
135
+ return `检索文件:${directory ? directory : ''}${exts ? '/*' : ''}${exts}${outputResult}`;
131
136
  }
132
137
  const outputResult = result ? (result?.success ? ` ✅` : result?.message || '❌') : '';
133
138
  return toolCall.function_name + outputResult;
package/dist/program.js CHANGED
@@ -12,12 +12,13 @@ const testAgent_1 = require("./testAgent");
12
12
  const tools_1 = require("./tools");
13
13
  const view_1 = require("./view");
14
14
  const user_1 = require("./user");
15
- // import { showCoverageView } from './view';
16
15
  const program = new commander_1.Command();
17
16
  program.name('testAgent').description('吃豆豆:单测智能体').version('1.0.1');
18
17
  // 屏蔽调试日志
19
18
  const logHandler = console.log;
20
- console.log = () => { };
19
+ if (process.env.NODE_ENV !== 'test') {
20
+ console.log = () => { };
21
+ }
21
22
  const tokenFile = path_1.default.join(os_1.default.homedir(), '.aiworktools');
22
23
  // token存储在系统用户文件夹的.ai-worktools
23
24
  (0, user_1.setTokenStorage)({
@@ -57,37 +58,57 @@ program
57
58
  program
58
59
  .command('start [projectRoot]')
59
60
  .description('启动单测智能体,根据源代码文件开始补全单测代码,直到覆盖率100%')
60
- .option('-p, --platform <string>', '智能体平台', 'jianguoke')
61
- .option('-r, --projectRoot <directory>', '源代码文件夹', (txt) => path_1.default.resolve(txt), process.cwd())
62
- .option('-t, --testDir <string>', '测试用例文件夹', 'test')
63
- .option('-s, --srcDir <string>', '测试用例文件夹', 'src')
64
- .option('-c, --coverageDir <string>', '覆盖率报告文件夹', 'coverage')
65
- .option('--autoCommit <boolean>', '覆盖率报告文件夹', (txt) => txt === 'true', true)
61
+ .option('--platform <string>', '智能体平台', 'jianguoke')
62
+ .option('--projectRoot <directory>', '源代码文件夹', (txt) => path_1.default.resolve(txt), process.cwd())
63
+ .option('--testDir <string>', '测试用例文件夹', 'test')
64
+ .option('--srcDir <string>', '测试用例文件夹', 'src')
65
+ .option('--coverageDir <string>', '覆盖率报告文件夹', 'coverage')
66
+ .option('-p, --packageManager <string>', '包管理器', 'npm')
67
+ .option('-f, --testFramework <string>', '测试框架', 'jest')
68
+ .option('-l, --language <string>', '开发语言', 'javascript')
69
+ .option('-a, --autoCommit <boolean>', '覆盖率报告文件夹', (txt) => txt === 'true', true)
66
70
  .option('--fullCoverageEnd <boolean>', '是否在达到100%覆盖率后结束', (txt) => txt === 'true', true)
67
71
  .action(async (projectRoot, options) => {
68
72
  let projectConfig;
73
+ const tasks = [];
69
74
  await (0, testAgent_1.startTestAgent)({
70
75
  ...options,
71
76
  projectRoot: projectRoot || options.projectRoot,
77
+ mode: 'increment',
72
78
  onReady: async (project) => {
73
79
  projectConfig = project;
74
80
  },
75
- onMessage: async (message) => {
81
+ onMessage: (message) => {
76
82
  logHandler(message);
77
83
  if (message.includes('当前代码总覆盖率')) {
78
- await (0, view_1.loadAndDisplayCoverage)(path_1.default.join(projectConfig.projectRoot, projectConfig.coverageDir, view_1.COVERAGE_FILE_PATH), 0);
84
+ const task = (0, view_1.loadAndDisplayCoverage)(path_1.default.join(projectConfig.projectRoot, projectConfig.coverageDir, view_1.COVERAGE_FILE_PATH), 0);
85
+ tasks.push(task);
86
+ task
87
+ .then(() => {
88
+ tasks.splice(tasks.indexOf(task), 1);
89
+ })
90
+ .catch(() => {
91
+ tasks.splice(tasks.indexOf(task), 1);
92
+ });
79
93
  }
80
94
  }
81
95
  });
96
+ await Promise.all(tasks);
82
97
  });
83
98
  program
84
- .command('showCoverage [projectRoot]')
99
+ .command('show [projectRoot]')
85
100
  .description('监控代码覆盖率报告')
86
- .option('-r, --projectRoot <directory>', '源代码文件夹', (txt) => path_1.default.resolve(txt), process.cwd())
87
- .option('-c, --coverageDir <string>', '覆盖率报告文件夹', 'coverage')
101
+ .option('--projectRoot <directory>', '源代码文件夹', (txt) => path_1.default.resolve(txt), process.cwd())
102
+ .option('--coverageDir <string>', '覆盖率报告文件夹', 'coverage')
103
+ .option('-w watch <boolean>', '是否监控代码覆盖率报告变化', false)
88
104
  .action(async (projectRoot, options) => {
89
105
  await (0, tools_1.runJestTests)(projectRoot || options.projectRoot);
90
- // await showCoverageView(path.join(options.projectRoot, options.coverageDir));
106
+ if (options.watch) {
107
+ await (0, view_1.showCoverageView)(path_1.default.join(projectRoot || options.projectRoot, options.coverageDir));
108
+ }
109
+ else {
110
+ await (0, view_1.loadAndDisplayCoverage)(path_1.default.join(projectRoot || options.projectRoot, options.coverageDir, view_1.COVERAGE_FILE_PATH));
111
+ }
91
112
  });
92
113
  exports.default = program;
93
114
  //# sourceMappingURL=program.js.map
package/dist/testAgent.js CHANGED
@@ -37,17 +37,16 @@ async function startTestAgent(options) {
37
37
  break;
38
38
  }
39
39
  if (!result.success) {
40
- // 单测执行失败,没有发现任何需要测试的代码
41
- if (result.noTestsFound) {
42
- log('没有发现任何需要测试的源代码...');
43
- if (!options.fullCoverageEnd) {
44
- await (0, tools_1.wait)(options.idleTimeout || 5000);
45
- continue;
46
- }
47
- else {
48
- break;
49
- }
50
- }
40
+ // // 单测执行失败,没有发现任何需要测试的代码
41
+ // if (result.noTestsFound) {
42
+ // log('没有发现任何需要测试的源代码...');
43
+ // if (!options.fullCoverageEnd) {
44
+ // await wait(options.idleTimeout || 5000);
45
+ // continue;
46
+ // } else {
47
+ // break;
48
+ // }
49
+ // }
51
50
  // 上一次执行必须有写入类操作,否则不会有新的单测覆盖率变化
52
51
  if (lastTestResult?.stderr === result.stderr &&
53
52
  toolCalls.every((toolCall) => !tools_1.modifyTools.includes(toolCall.functionName))) {
@@ -58,7 +57,7 @@ async function startTestAgent(options) {
58
57
  lastTestResult = result;
59
58
  // 单测执行失败,比如代码编译不通过
60
59
  log('单测执行失败,开始修复...');
61
- await (0, agents_1.startAgent)(options.platform, 'fixconfig', (0, agents_1.fixErrorPrompt)(result.stderr), projectConfig, options.withConversation, ctrl, options.onMessage, async (tool) => {
60
+ await (0, agents_1.startAgent)(options.platform, 'fixconfig', (0, agents_1.fixErrorPrompt)(result.stderr), projectConfig, options.withConversation, ctrl, options.mode, options.onMessage, async (tool) => {
62
61
  toolCalls.push(tool);
63
62
  });
64
63
  if (ctrl?.signal.aborted) {
@@ -86,7 +85,7 @@ async function startTestAgent(options) {
86
85
  log(`发现有失败的单测${path_1.default.basename(failedTest.testFilePath)},开始修复...`);
87
86
  await (0, agents_1.startAgent)(options.platform,
88
87
  // 没有testName说明是修复单测代码本身问题
89
- failedTest.testName ? 'fixbug' : 'fixconfig', await (0, agents_1.fixCodePrompt)(failedTest), projectConfig, options.withConversation, ctrl, options.onMessage, async (tool) => {
88
+ failedTest.testName ? 'fixbug' : 'fixconfig', await (0, agents_1.fixCodePrompt)(failedTest), projectConfig, options.withConversation, ctrl, options.mode, options.onMessage, async (tool) => {
90
89
  toolCalls.push(tool);
91
90
  });
92
91
  if (ctrl?.signal.aborted) {
@@ -111,7 +110,7 @@ async function startTestAgent(options) {
111
110
  continue;
112
111
  }
113
112
  log(`发现有未覆盖行,开始补充...`, filePath);
114
- await (0, agents_1.startAgent)(options.platform, 'addtest', await (0, agents_1.addUncoveredLinePrompt)(filePath, uncoveredLineFile), projectConfig, options.withConversation, ctrl, options.onMessage, async (tool) => {
113
+ await (0, agents_1.startAgent)(options.platform, 'addtest', await (0, agents_1.addUncoveredLinePrompt)(filePath, uncoveredLineFile), projectConfig, options.withConversation, ctrl, options.mode, options.onMessage, async (tool) => {
115
114
  toolCalls.push(tool);
116
115
  });
117
116
  if (ctrl?.signal.aborted) {
package/dist/tools/git.js CHANGED
@@ -43,7 +43,7 @@ async function clone(repoPath, remoteUrl, localPath, branch) {
43
43
  console.log(`成功克隆仓库: ${remoteUrl} 到 ${clonePath}`);
44
44
  }
45
45
  catch (error) {
46
- console.error(`克隆仓库失败: ${error}`);
46
+ console.log(`克隆仓库失败: ${error}`);
47
47
  throw error;
48
48
  }
49
49
  }
@@ -59,7 +59,7 @@ async function add(files = '.', repoPath) {
59
59
  console.log(`成功添加文件到暂存区: ${Array.isArray(files) ? files.join(', ') : files}`);
60
60
  }
61
61
  catch (error) {
62
- console.error(`添加文件失败: ${error}`);
62
+ console.log(`添加文件失败: ${error}`);
63
63
  throw error;
64
64
  }
65
65
  }
@@ -76,7 +76,7 @@ async function commit(repoPath, message, files) {
76
76
  console.log(`提交成功: ${result?.summary}`);
77
77
  }
78
78
  catch (error) {
79
- console.error(`提交失败: ${error}`);
79
+ console.log(`提交失败: ${error}`);
80
80
  throw error;
81
81
  }
82
82
  }
@@ -94,7 +94,7 @@ async function pull(repoPath, remote = 'origin', branch) {
94
94
  console.log(`拉取成功: ${result?.summary}`);
95
95
  }
96
96
  catch (error) {
97
- console.error(`拉取失败: ${error}`);
97
+ console.log(`拉取失败: ${error}`);
98
98
  throw error;
99
99
  }
100
100
  }
@@ -112,7 +112,7 @@ async function push(repoPath, remote = 'origin', branch) {
112
112
  console.log(`推送成功: ${remote}/${currentBranch}`);
113
113
  }
114
114
  catch (error) {
115
- console.error(`推送失败: ${error}`);
115
+ console.log(`推送失败: ${error}`);
116
116
  // 处理常见的推送失败情况(如远程分支有新提交)
117
117
  if (error.message.includes('non-fast-forward')) {
118
118
  console.log('提示: 远程分支有新提交,建议先拉取再推送');
@@ -132,7 +132,7 @@ async function createBranch(branchName, repoPath) {
132
132
  console.log(`成功创建并切换到分支: ${branchName}`);
133
133
  }
134
134
  catch (error) {
135
- console.error(`创建分支失败: ${error}`);
135
+ console.log(`创建分支失败: ${error}`);
136
136
  throw error;
137
137
  }
138
138
  }
@@ -148,7 +148,7 @@ async function switchBranch(branchName, repoPath) {
148
148
  console.log(`成功切换到分支: ${branchName}`);
149
149
  }
150
150
  catch (error) {
151
- console.error(`切换分支失败: ${error}`);
151
+ console.log(`切换分支失败: ${error}`);
152
152
  throw error;
153
153
  }
154
154
  }
@@ -162,7 +162,7 @@ async function getStatus(repoPath) {
162
162
  return await git.status();
163
163
  }
164
164
  catch (error) {
165
- console.error(`获取仓库状态失败: ${error}`);
165
+ console.log(`获取仓库状态失败: ${error}`);
166
166
  throw error;
167
167
  }
168
168
  }
@@ -179,7 +179,7 @@ async function addRemote(remoteName, remoteUrl, repoPath) {
179
179
  console.log(`成功添加远程仓库: ${remoteName} -> ${remoteUrl}`);
180
180
  }
181
181
  catch (error) {
182
- console.error(`添加远程仓库失败: ${error}`);
182
+ console.log(`添加远程仓库失败: ${error}`);
183
183
  throw error;
184
184
  }
185
185
  }
@@ -197,7 +197,7 @@ async function setUserInfo(username, email, repoPath) {
197
197
  console.log(`已设置用户信息: ${username} <${email}>`);
198
198
  }
199
199
  catch (error) {
200
- console.error(`设置用户信息失败: ${error}`);
200
+ console.log(`设置用户信息失败: ${error}`);
201
201
  throw error;
202
202
  }
203
203
  }
@@ -67,7 +67,8 @@ function parseJavaScriptFile(filePath) {
67
67
  * @param ast AST 对象
68
68
  * @returns 导出函数名称数组
69
69
  */
70
- function extractExportedFunctions(ast) {
70
+ function extractExportedFunctions(filePath) {
71
+ const ast = typeof filePath === 'string' ? parseJavaScriptFile(filePath) : filePath;
71
72
  const exportedFunctions = [];
72
73
  walk.simple(ast, {
73
74
  // 处理命名函数导出
@@ -101,7 +102,8 @@ function extractExportedFunctions(ast) {
101
102
  * @param ast AST 对象
102
103
  * @returns 导出类名称数组
103
104
  */
104
- function extractExportedClasses(ast) {
105
+ function extractExportedClasses(filePath) {
106
+ const ast = typeof filePath === 'string' ? parseJavaScriptFile(filePath) : filePath;
105
107
  const exportedClasses = [];
106
108
  walk.simple(ast, {
107
109
  // 处理命名类导出
@@ -135,7 +137,8 @@ function extractExportedClasses(ast) {
135
137
  * @param ast AST 对象
136
138
  * @returns 导入模块路径数组
137
139
  */
138
- function extractImportedModules(ast) {
140
+ function extractImportedModules(filePath) {
141
+ const ast = typeof filePath === 'string' ? parseJavaScriptFile(filePath) : filePath;
139
142
  const importedModules = [];
140
143
  walk.simple(ast, {
141
144
  ImportDeclaration(node) {
@@ -42,7 +42,6 @@ exports.getFailedTests = getFailedTests;
42
42
  exports.parseLcovForUncoveredLines = parseLcovForUncoveredLines;
43
43
  exports.readCoverageSummary = readCoverageSummary;
44
44
  exports.generateUncoveredLinesReport = generateUncoveredLinesReport;
45
- exports.extractFailedTests = extractFailedTests;
46
45
  exports.getFailedTestSummaries = getFailedTestSummaries;
47
46
  exports.findTestErrorDetails = findTestErrorDetails;
48
47
  exports.clearJsonReportCache = clearJsonReportCache;
@@ -90,7 +89,7 @@ async function createJestConfigFile(configPath, options = {}) {
90
89
  'src/**/*.{js,jsx,ts,tsx}', // 例如:src 目录下所有 JS/TS 文件
91
90
  '!src/**/*.d.ts', // 排除类型定义文件(可选)
92
91
  '!src/mocks/**', // 排除 mock 文件(可选)
93
- // '!src/**/index.{js,ts}', // 排除入口文件(可选,根据需求调整)
92
+ '!src/**/index.{js,ts}', // 排除入口文件(可选,根据需求调整)
94
93
  '!**/node_modules/**', // 排除 node_modules
95
94
  '!**/vendor/**' // 排除第三方依赖(如有)
96
95
  ],
@@ -129,6 +128,9 @@ async function getTestResultsState(projectRoot, coverageDir = 'coverage') {
129
128
  */
130
129
  async function runJestTests(projectRoot, coverageDir = 'coverage', jestConfigPath) {
131
130
  coverageDir = coverageDir || 'coverage';
131
+ if (!(await fileExists(path.join(projectRoot, 'package.json')))) {
132
+ throw new Error('当前目录下不存在 package.json 文件,请确保当前目录为项目根目录。');
133
+ }
132
134
  return new Promise((resolve, reject) => {
133
135
  const now = Date.now();
134
136
  const testResultsFile = path.join(projectRoot, coverageDir, 'test-results.json');
@@ -185,6 +185,7 @@ async function addScript(projectPath, scriptName, scriptCommand) {
185
185
  */
186
186
  async function runTest(projectPath, args = [], manager = 'yarn') {
187
187
  try {
188
+ args = Array.isArray(args) ? args : args.split(' ');
188
189
  const resolvedPath = path_1.default.resolve(projectPath);
189
190
  const runCmd = getRunScriptCommand('test', args, manager);
190
191
  const [cmd, ...cmdArgs] = runCmd.split(' ').filter(Boolean);
@@ -279,7 +280,8 @@ async function checkDependency(projectRoot, packageName, isDev = false) {
279
280
  async function setupTestEnvironment(projectRoot, packageManager, devLanguage, testFramework) {
280
281
  // 验证参数
281
282
  validateParameters(packageManager, devLanguage, testFramework);
282
- const packageJsonPath = path_1.default.join(projectRoot, 'package.json');
283
+ const resolvedPath = path_1.default.resolve(projectRoot);
284
+ const packageJsonPath = path_1.default.join(resolvedPath, 'package.json');
283
285
  // 异步检查并初始化package.json
284
286
  try {
285
287
  await promises_1.default.access(packageJsonPath);
@@ -287,29 +289,29 @@ async function setupTestEnvironment(projectRoot, packageManager, devLanguage, te
287
289
  catch {
288
290
  console.log('未找到package.json,初始化项目...');
289
291
  const initCommand = packageManager === 'yarn' ? 'yarn init -y' : `${packageManager} init -y`;
290
- await executeCommand(projectRoot, initCommand, '初始化项目');
292
+ await executeCommand(resolvedPath, initCommand, '初始化项目');
291
293
  }
292
294
  // 如果是TypeScript项目,检查并安装TypeScript
293
295
  if (devLanguage === 'typescript') {
294
296
  console.log('检查TypeScript是否安装...');
295
- const hasTypeScript = await checkDependency(projectRoot, 'typescript', true);
297
+ const hasTypeScript = await checkDependency(resolvedPath, 'typescript', true);
296
298
  if (!hasTypeScript) {
297
299
  const installCmd = getInstallCommand(['typescript', '@types/node'], true, packageManager);
298
- await executeCommand(projectRoot, installCmd, '安装TypeScript及类型定义');
300
+ await executeCommand(resolvedPath, installCmd, '安装TypeScript及类型定义');
299
301
  // 异步检查并创建tsconfig.json
300
- const tsconfigPath = path_1.default.join(projectRoot, 'tsconfig.json');
302
+ const tsconfigPath = path_1.default.join(resolvedPath, 'tsconfig.json');
301
303
  try {
302
304
  await promises_1.default.access(tsconfigPath);
303
305
  }
304
306
  catch {
305
- await executeCommand(projectRoot, 'npx tsc --init', '创建tsconfig.json');
307
+ await executeCommand(resolvedPath, 'npx tsc --init', '创建tsconfig.json');
306
308
  }
307
309
  }
308
310
  }
309
311
  // 检查并安装测试框架
310
312
  console.log(`检查${testFramework}是否安装...`);
311
- const hasTestFramework = await checkDependency(projectRoot, testFramework, true);
312
- const hasTestTypes = devLanguage === 'typescript' ? await checkDependency(projectRoot, `@types/${testFramework}`, true) : true;
313
+ const hasTestFramework = await checkDependency(resolvedPath, testFramework, true);
314
+ const hasTestTypes = devLanguage === 'typescript' ? await checkDependency(resolvedPath, `@types/${testFramework}`, true) : true;
313
315
  const packagesToInstall = [];
314
316
  if (!hasTestFramework) {
315
317
  packagesToInstall.push(testFramework);
@@ -342,16 +344,19 @@ async function setupTestEnvironment(projectRoot, packageManager, devLanguage, te
342
344
  if (packagesToInstall.length > 0) {
343
345
  const installCmd = getInstallCommand(packagesToInstall, true, packageManager);
344
346
  await executeCommand(projectRoot, installCmd, `安装${testFramework}及相关依赖`);
345
- // 框架特定的配置
346
- let exists = false;
347
- try {
348
- await promises_1.default.access(path_1.default.join(projectRoot, 'jest.config.js'));
349
- exists = true;
350
- }
351
- catch { }
352
- if (testFramework === 'jest' && !exists) {
353
- await executeCommand(projectRoot, 'npx ts-jest config:init', '配置Jest TypeScript支持');
354
- }
347
+ // // 框架特定的配置
348
+ // let exists = false;
349
+ // try {
350
+ // await fs.access(path.join(projectRoot, 'jest.config.js'));
351
+ // exists = true;
352
+ // } catch {}
353
+ // if (testFramework === 'jest') {
354
+ // if (!exists) {
355
+ // }
356
+ // if (devLanguage === 'typescript') {
357
+ // await executeCommand(projectRoot, 'npx ts-jest config:init', '配置Jest TypeScript支持');
358
+ // }
359
+ // }
355
360
  console.log('✅ 测试环境搭建完成!');
356
361
  }
357
362
  // 检查并添加test脚本