ai-worktool 1.0.18 → 1.0.19
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/CHANGELOG.md +4 -1
- package/dist/agents/chanjet.js +7 -2
- package/dist/agents/index.js +2 -2
- package/dist/agents/jianguoke.js +29 -9
- package/dist/agents/toolCall.js +12 -7
- package/dist/program.js +35 -14
- package/dist/testAgent.js +13 -14
- package/dist/tools/git.js +10 -10
- package/dist/tools/javascript.js +6 -3
- package/dist/tools/jest.js +4 -2
- package/dist/tools/package.js +23 -18
- package/dist/tools/project.js +29 -18
- package/dist/user.js +14 -0
- package/dist/view.js +20 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
## 1.0.
|
1
|
+
## 1.0.19 (2025-08-10)
|
2
2
|
|
3
3
|
|
4
4
|
### Bug Fixes
|
@@ -78,6 +78,7 @@
|
|
78
78
|
* JEST无法输出colors ([3a1bd24](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/3a1bd24ba0b9b6d78dc9cdb5958f3262d08d9ded))
|
79
79
|
* **login:** 更新登录地址 ([36ba18d](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/36ba18db3f2acb42f4964de2e93a6a093909bec2))
|
80
80
|
* **login:** 更新客户端 ID ([bfeac59](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/bfeac590f8bae37c1a123c8356dd600ed9add4ad))
|
81
|
+
* **user:** 优化 JWT 解析并添加过期检查 ([33edb60](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/33edb600bd0a114b152e631d2a9259e4b04858bd))
|
81
82
|
* **webview:** 新增文件无法观察问题 ([8f18813](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/8f188134861f5e151d2bcd0e7a1dda5d6a1e0268))
|
82
83
|
* writeFile内部可以覆盖 ([ea4ee44](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/ea4ee442e7f3ec5cdfef5cf9142c8036772aaa11))
|
83
84
|
|
@@ -138,6 +139,7 @@
|
|
138
139
|
* **agent:** 增加工具调用回调并优化测试流程 ([1cae77e](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/1cae77e7bf05c3f72771d29568034eaecfea3c8e))
|
139
140
|
* **agent:** 增加智能体增量回复功能 ([edd406b](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/edd406b905b930a7943c9697248150f9a4505e08))
|
140
141
|
* **ai-worktool:** 重构 Webview 交互逻辑 ([d118dec](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/d118dec14d3ca3644f41a34db4a5b028987df2db))
|
142
|
+
* **commands:** 在打开订单和使用页面时添加 token 参数 ([f52c7a3](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/f52c7a349988eeec08370b4fb673bc479256ab72))
|
141
143
|
* **commands:** 在打开订单和使用页面时添加 token 参数 ([c20e809](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/c20e809cec5a0c2c1ca886c5414730ce4893fabd))
|
142
144
|
* **configuration:** 增加包管理器、测试框架和编程语言选择配置项 ([ec3cceb](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/ec3cceba695485010c5c710894ea2e49f57737f8))
|
143
145
|
* **dify-plugin:** 新增重命名文件或目录的功能 ([9bb6961](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/9bb69613a28aba6859f72b14b2aa1a586ac9ca70))
|
@@ -148,6 +150,7 @@
|
|
148
150
|
* **program:** 增加代码覆盖率报告展示功能 ([60da9b5](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/60da9b59e4b913695daa8697dd9aeec16cf78a5f))
|
149
151
|
* **program:** 增加代码覆盖率报告展示功能 ([f693e27](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/f693e272ef0fe7e58083d2589339af17e45cff95))
|
150
152
|
* **project:** 优化项目类型检测逻辑 ([9bc024d](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/9bc024deeed239c05c9cab810f9f12a6a3306aab))
|
153
|
+
* **project:** 增加加载 aicoder 配置文件功能 ([b4dc70b](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/b4dc70b3afa88cf45135cf56e63cbb2ffa2fed82))
|
151
154
|
* Refactor CLI and program structure, update commands for testing and fixing code ([8dcbb19](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/8dcbb191fc8a95a40e07647de337452935926b10))
|
152
155
|
* **test/demo:** 添加用户成绩 API 路由 ([6f838c7](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/6f838c7fda9924b1e5a7556d0a429427a652b985))
|
153
156
|
* **test:** 添加 demo ([403c8fe](https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools/commits/403c8fe37d39a21936561e743087d6b25ab6ad56))
|
package/dist/agents/chanjet.js
CHANGED
@@ -207,7 +207,7 @@ const stopAgent = async (taskId) => {
|
|
207
207
|
};
|
208
208
|
exports.stopAgent = stopAgent;
|
209
209
|
// 获取机器人响应
|
210
|
-
const startAgent = async (name, userMessage, vars, withConversation, ctrl, onMessage, onToolCall) => {
|
210
|
+
const startAgent = async (name, userMessage, vars, withConversation, ctrl, mode, onMessage, onToolCall) => {
|
211
211
|
if (withConversation !== true) {
|
212
212
|
(0, exports.setConversationId)(''); // 每次重来
|
213
213
|
}
|
@@ -353,7 +353,12 @@ const startAgent = async (name, userMessage, vars, withConversation, ctrl, onMes
|
|
353
353
|
break;
|
354
354
|
case 'text':
|
355
355
|
if (contentObj.type === 'text' && contentObj.text) {
|
356
|
-
|
356
|
+
if (mode === 'full') {
|
357
|
+
botResponse += contentObj.text;
|
358
|
+
}
|
359
|
+
else {
|
360
|
+
botResponse = contentObj.text;
|
361
|
+
}
|
357
362
|
await onMessage?.(botResponse, name + botId);
|
358
363
|
// addMessage(botResponse, 'bot');
|
359
364
|
}
|
package/dist/agents/index.js
CHANGED
@@ -62,12 +62,12 @@ const setConversationId = (id) => {
|
|
62
62
|
platform.setConversationId(id);
|
63
63
|
};
|
64
64
|
exports.setConversationId = setConversationId;
|
65
|
-
async function startAgent(currentPlatform, name, question, vars, withConversation, ctrl, onMessage, onToolCall) {
|
65
|
+
async function startAgent(currentPlatform, name, question, vars, withConversation, ctrl, mode, onMessage, onToolCall) {
|
66
66
|
const platform = platforms[currentPlatform] || getPlatform();
|
67
67
|
if (!platform) {
|
68
68
|
throw new Error(`智能体 "${currentPlatform}" 不支持`);
|
69
69
|
}
|
70
|
-
await platform.startAgent.apply(null, [name, question, vars, withConversation, ctrl, onMessage, onToolCall]);
|
70
|
+
await platform.startAgent.apply(null, [name, question, vars, withConversation, ctrl, mode, onMessage, onToolCall]);
|
71
71
|
console.log(`智能体 "${currentPlatform}" 调用完成`);
|
72
72
|
}
|
73
73
|
//# sourceMappingURL=index.js.map
|
package/dist/agents/jianguoke.js
CHANGED
@@ -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
|
-
|
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
|
-
|
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),
|
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),
|
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.
|
194
|
+
err = data.error;
|
175
195
|
}
|
176
196
|
}
|
177
197
|
});
|
package/dist/agents/toolCall.js
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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('
|
61
|
-
.option('
|
62
|
-
.option('
|
63
|
-
.option('
|
64
|
-
.option('
|
65
|
-
.option('--
|
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:
|
81
|
+
onMessage: (message) => {
|
76
82
|
logHandler(message);
|
77
83
|
if (message.includes('当前代码总覆盖率')) {
|
78
|
-
|
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('
|
99
|
+
.command('show [projectRoot]')
|
85
100
|
.description('监控代码覆盖率报告')
|
86
|
-
.option('
|
87
|
-
.option('
|
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
|
-
|
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
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
200
|
+
console.log(`设置用户信息失败: ${error}`);
|
201
201
|
throw error;
|
202
202
|
}
|
203
203
|
}
|
package/dist/tools/javascript.js
CHANGED
@@ -67,7 +67,8 @@ function parseJavaScriptFile(filePath) {
|
|
67
67
|
* @param ast AST 对象
|
68
68
|
* @returns 导出函数名称数组
|
69
69
|
*/
|
70
|
-
function extractExportedFunctions(
|
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(
|
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(
|
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) {
|
package/dist/tools/jest.js
CHANGED
@@ -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
|
-
|
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');
|
package/dist/tools/package.js
CHANGED
@@ -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
|
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(
|
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(
|
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(
|
300
|
+
await executeCommand(resolvedPath, installCmd, '安装TypeScript及类型定义');
|
299
301
|
// 异步检查并创建tsconfig.json
|
300
|
-
const tsconfigPath = path_1.default.join(
|
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(
|
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(
|
312
|
-
const hasTestTypes = devLanguage === 'typescript' ? await checkDependency(
|
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
|
-
|
349
|
-
|
350
|
-
}
|
351
|
-
|
352
|
-
if (
|
353
|
-
|
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脚本
|
package/dist/tools/project.js
CHANGED
@@ -20,7 +20,7 @@ const path_1 = __importDefault(require("path"));
|
|
20
20
|
*/
|
21
21
|
async function detectProjectConfig(projectRoot, defaultConfig) {
|
22
22
|
defaultConfig = defaultConfig || {};
|
23
|
-
|
23
|
+
let config = {
|
24
24
|
projectRoot: defaultConfig.projectRoot || projectRoot,
|
25
25
|
projectType: defaultConfig.projectType || 'unknown',
|
26
26
|
packageManager: defaultConfig.packageManager || 'unknown',
|
@@ -33,22 +33,32 @@ async function detectProjectConfig(projectRoot, defaultConfig) {
|
|
33
33
|
testConfigFile: defaultConfig.testConfigFile || undefined,
|
34
34
|
isInstalled: defaultConfig.isInstalled || undefined
|
35
35
|
};
|
36
|
+
if (!projectRoot) {
|
37
|
+
throw new Error('项目文件夹未知.');
|
38
|
+
}
|
39
|
+
// 检查文件夹是否存在
|
40
|
+
if (!(await directoryExists(projectRoot))) {
|
41
|
+
throw new Error(`项目文件夹 "${projectRoot}" 不存在.`);
|
42
|
+
}
|
43
|
+
// 检测包管理工具
|
44
|
+
await detectPackageManager(projectRoot, config);
|
36
45
|
// 首先检查项目是否已安装
|
37
46
|
await checkProjectInstalled(projectRoot, config);
|
38
|
-
//
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
// 检测使用的开发语言
|
48
|
+
await detectLanguages(projectRoot, config);
|
49
|
+
// 检测源代码目录
|
50
|
+
await detectSrcDir(projectRoot, config);
|
51
|
+
// 检测测试相关配置
|
52
|
+
await detectTestConfig(projectRoot, config);
|
53
|
+
// 检测项目类型(前端/后端/全栈)
|
54
|
+
await detectProjectType(projectRoot, config);
|
55
|
+
// 检测框架信息
|
56
|
+
await detectFramework(projectRoot, config);
|
57
|
+
// 检查项目目录中是否有aicoder.config.json有的直接加载作为配置信息
|
58
|
+
if (await fileExists(path_1.default.join(projectRoot, 'aicoder.config.json'))) {
|
59
|
+
const aicoderConfig = JSON.parse(await promises_1.default.readFile(path_1.default.join(projectRoot, 'aicoder.config.json'), 'utf8'));
|
60
|
+
// 合并配置信息
|
61
|
+
config = { ...config, ...aicoderConfig };
|
52
62
|
}
|
53
63
|
return config;
|
54
64
|
}
|
@@ -81,7 +91,7 @@ async function detectPackageManager(projectRoot, config) {
|
|
81
91
|
return;
|
82
92
|
}
|
83
93
|
}
|
84
|
-
|
94
|
+
config.packageManager = 'npm';
|
85
95
|
}
|
86
96
|
/**
|
87
97
|
* 检测开发语言 - 排除node_modules和隐藏目录
|
@@ -257,7 +267,7 @@ async function detectTestConfig(projectRoot, config) {
|
|
257
267
|
const testDirPath = path_1.default.join(projectRoot, config.testDir);
|
258
268
|
if (await directoryExists(testDirPath)) {
|
259
269
|
// 检测测试目录中实际存在的文件模式
|
260
|
-
const detectedPatterns = await detectExistingTestPatterns(testDirPath, projectRoot, extensions);
|
270
|
+
const detectedPatterns = await detectExistingTestPatterns(testDirPath, projectRoot, Array.from(extensions));
|
261
271
|
config.testFilePatterns = detectedPatterns;
|
262
272
|
}
|
263
273
|
}
|
@@ -271,6 +281,7 @@ async function detectTestConfig(projectRoot, config) {
|
|
271
281
|
*/
|
272
282
|
async function detectExistingTestPatterns(testDir, projectRoot, extensions) {
|
273
283
|
const patterns = new Set();
|
284
|
+
const extensionSets = new Set(Array.isArray(extensions) ? extensions : extensions.split(','));
|
274
285
|
const relativeTestDir = path_1.default.relative(projectRoot, testDir);
|
275
286
|
// 递归检查测试目录中的文件
|
276
287
|
async function checkDir(currentDir) {
|
@@ -283,7 +294,7 @@ async function detectExistingTestPatterns(testDir, projectRoot, extensions) {
|
|
283
294
|
else if (entry.isFile()) {
|
284
295
|
// 检查文件是否匹配测试文件命名规范
|
285
296
|
const extMatch = entry.name.match(/\.([^.]+)$/);
|
286
|
-
if (extMatch &&
|
297
|
+
if (extMatch && extensionSets.has(extMatch[1])) {
|
287
298
|
const fileName = entry.name.replace(/\.[^.]+$/, '');
|
288
299
|
if (fileName.endsWith('.test') || fileName.endsWith('.spec')) {
|
289
300
|
// 生成对应的glob模式
|
package/dist/user.js
CHANGED
@@ -440,6 +440,17 @@ async function getUserData() {
|
|
440
440
|
}
|
441
441
|
return null;
|
442
442
|
}
|
443
|
+
function isTokenExpired(payload) {
|
444
|
+
if (!payload || !payload.exp) {
|
445
|
+
// 没有过期时间字段,视为过期
|
446
|
+
return true;
|
447
|
+
}
|
448
|
+
// JWT的exp是秒级时间戳,需要转换为毫秒
|
449
|
+
const expirationTime = payload.exp * 1000;
|
450
|
+
const currentTime = Date.now();
|
451
|
+
// 检查是否已过期(提前30秒过期,避免网络延迟问题)
|
452
|
+
return currentTime >= expirationTime - 30000;
|
453
|
+
}
|
443
454
|
/**
|
444
455
|
* 解析JWT获取其中的信息
|
445
456
|
* @param token JWT字符串
|
@@ -449,6 +460,9 @@ function decodeJwt(token) {
|
|
449
460
|
try {
|
450
461
|
// 不验证签名,仅解码获取信息
|
451
462
|
const decoded = jsonwebtoken_1.default.decode(token);
|
463
|
+
if (isTokenExpired(decoded)) {
|
464
|
+
return null;
|
465
|
+
}
|
452
466
|
return decoded;
|
453
467
|
}
|
454
468
|
catch (error) {
|
package/dist/view.js
CHANGED
@@ -7,10 +7,11 @@ exports.COVERAGE_FILE_PATH = void 0;
|
|
7
7
|
exports.showCoverageView = showCoverageView;
|
8
8
|
exports.stopMonitoring = stopMonitoring;
|
9
9
|
exports.loadAndDisplayCoverage = loadAndDisplayCoverage;
|
10
|
+
exports.disposeTimer = disposeTimer;
|
10
11
|
const fs_1 = __importDefault(require("fs"));
|
11
12
|
const path_1 = __importDefault(require("path"));
|
12
13
|
const table_1 = require("table");
|
13
|
-
const
|
14
|
+
const cli_color_1 = __importDefault(require("cli-color"));
|
14
15
|
let REFRESH_INTERVAL = 3000;
|
15
16
|
// 存储上一次的覆盖率数据
|
16
17
|
let previousCoverage = null;
|
@@ -25,7 +26,7 @@ exports.COVERAGE_FILE_PATH = 'coverage-summary.json';
|
|
25
26
|
// 主函数
|
26
27
|
async function showCoverageView(rootDir, coverageFilePath = exports.COVERAGE_FILE_PATH) {
|
27
28
|
const coveragePath = path_1.default.join(rootDir, coverageFilePath);
|
28
|
-
console.log(
|
29
|
+
console.log(cli_color_1.default.cyan('开始监控覆盖率数据...' + coveragePath));
|
29
30
|
// 初始加载
|
30
31
|
await loadAndDisplayCoverage(coveragePath);
|
31
32
|
// 设置文件监听
|
@@ -50,6 +51,7 @@ function stopMonitoring() {
|
|
50
51
|
refreshTimer = null;
|
51
52
|
}
|
52
53
|
}
|
54
|
+
let timer = null;
|
53
55
|
// 加载并显示覆盖率数据
|
54
56
|
async function loadAndDisplayCoverage(coveragePath, refreshTimeout = REFRESH_INTERVAL) {
|
55
57
|
try {
|
@@ -61,7 +63,8 @@ async function loadAndDisplayCoverage(coveragePath, refreshTimeout = REFRESH_INT
|
|
61
63
|
changedCells = compareCoverage(previousCoverage, coverage);
|
62
64
|
// 如果有变化,3秒后清除标记
|
63
65
|
if (changedCells.length > 0) {
|
64
|
-
setTimeout(() => {
|
66
|
+
timer = setTimeout(() => {
|
67
|
+
timer = null;
|
65
68
|
changedCells = [];
|
66
69
|
loadAndDisplayCoverage(coveragePath);
|
67
70
|
}, refreshTimeout);
|
@@ -73,7 +76,7 @@ async function loadAndDisplayCoverage(coveragePath, refreshTimeout = REFRESH_INT
|
|
73
76
|
renderTable(tableData);
|
74
77
|
}
|
75
78
|
catch (error) {
|
76
|
-
console.error(
|
79
|
+
console.error(cli_color_1.default.red(`加载覆盖率数据时出错: ${error.message}`));
|
77
80
|
}
|
78
81
|
}
|
79
82
|
// 读取覆盖率文件
|
@@ -102,7 +105,7 @@ function generateTableData(coverage) {
|
|
102
105
|
filePaths.sort();
|
103
106
|
// 添加总计行
|
104
107
|
tableData.push([
|
105
|
-
|
108
|
+
cli_color_1.default.bold('总计'),
|
106
109
|
formatPercentage(coverage.total.lines.pct),
|
107
110
|
formatPercentage(coverage.total.statements.pct),
|
108
111
|
formatPercentage(coverage.total.functions.pct),
|
@@ -126,17 +129,17 @@ function generateTableData(coverage) {
|
|
126
129
|
// 格式化百分比并应用颜色
|
127
130
|
function formatPercentage(pct) {
|
128
131
|
if (pct === 'Unknown')
|
129
|
-
return
|
132
|
+
return cli_color_1.default.italic('Unknown');
|
130
133
|
let formatted = pct.toFixed(2) + '%';
|
131
134
|
// 根据覆盖率设置颜色
|
132
135
|
if (pct < 50) {
|
133
|
-
formatted =
|
136
|
+
formatted = cli_color_1.default.red(formatted);
|
134
137
|
}
|
135
138
|
else if (pct < 80) {
|
136
|
-
formatted =
|
139
|
+
formatted = cli_color_1.default.yellow(formatted);
|
137
140
|
}
|
138
141
|
else {
|
139
|
-
formatted =
|
142
|
+
formatted = cli_color_1.default.green(formatted);
|
140
143
|
}
|
141
144
|
return formatted;
|
142
145
|
}
|
@@ -176,7 +179,7 @@ function renderTable(tableData) {
|
|
176
179
|
return row.map((cell, colIndex) => {
|
177
180
|
// 检查是否是变化的单元格
|
178
181
|
if (changedCells.some(([r, c]) => r === rowIndex && c === colIndex)) {
|
179
|
-
return
|
182
|
+
return cli_color_1.default.bgRed.white(cell);
|
180
183
|
}
|
181
184
|
return cell;
|
182
185
|
});
|
@@ -210,7 +213,13 @@ function renderTable(tableData) {
|
|
210
213
|
};
|
211
214
|
// 清除控制台并打印表格
|
212
215
|
console.clear();
|
213
|
-
console.log(
|
216
|
+
console.log(cli_color_1.default.cyan('单测覆盖率监控工具 - 最后更新: ' + new Date().toLocaleString()));
|
214
217
|
output((0, table_1.table)(markedData, tableConfig));
|
215
218
|
}
|
219
|
+
function disposeTimer() {
|
220
|
+
if (timer) {
|
221
|
+
clearTimeout(timer);
|
222
|
+
timer = null;
|
223
|
+
}
|
224
|
+
}
|
216
225
|
//# sourceMappingURL=view.js.map
|