claude-coder 1.8.2 → 1.8.3
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/README.md +167 -167
- package/bin/cli.js +172 -172
- package/package.json +52 -52
- package/src/commands/auth.js +290 -240
- package/src/commands/setup-modules/helpers.js +99 -99
- package/src/commands/setup-modules/index.js +25 -25
- package/src/commands/setup-modules/mcp.js +94 -94
- package/src/commands/setup-modules/provider.js +260 -260
- package/src/commands/setup-modules/safety.js +61 -61
- package/src/commands/setup-modules/simplify.js +52 -52
- package/src/commands/setup.js +172 -172
- package/src/common/assets.js +236 -236
- package/src/common/config.js +125 -125
- package/src/common/constants.js +55 -55
- package/src/common/indicator.js +222 -222
- package/src/common/interaction.js +170 -170
- package/src/common/logging.js +77 -77
- package/src/common/sdk.js +50 -50
- package/src/common/tasks.js +88 -88
- package/src/common/utils.js +161 -161
- package/src/core/coding.js +55 -55
- package/src/core/context.js +117 -117
- package/src/core/go.js +310 -310
- package/src/core/harness.js +484 -484
- package/src/core/hooks.js +533 -533
- package/src/core/init.js +171 -171
- package/src/core/plan.js +325 -325
- package/src/core/prompts.js +227 -227
- package/src/core/query.js +49 -49
- package/src/core/repair.js +46 -46
- package/src/core/runner.js +195 -195
- package/src/core/scan.js +89 -89
- package/src/core/session.js +56 -56
- package/src/core/simplify.js +53 -52
- package/templates/bash-process.md +12 -12
- package/templates/codingSystem.md +65 -65
- package/templates/codingUser.md +17 -17
- package/templates/coreProtocol.md +29 -29
- package/templates/goSystem.md +130 -130
- package/templates/guidance.json +52 -52
- package/templates/planSystem.md +78 -78
- package/templates/planUser.md +8 -8
- package/templates/playwright.md +16 -16
- package/templates/requirements.example.md +57 -57
- package/templates/scanSystem.md +120 -120
- package/templates/scanUser.md +10 -10
- package/templates/test_rule.md +194 -194
package/src/common/indicator.js
CHANGED
|
@@ -1,222 +1,222 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { COLOR } = require('./config');
|
|
4
|
-
const { localTimestamp, truncatePath } = require('./utils');
|
|
5
|
-
|
|
6
|
-
const SPINNERS = ['⠋', '⠙', '⠸', '⠴', '⠦', '⠇'];
|
|
7
|
-
|
|
8
|
-
class Indicator {
|
|
9
|
-
constructor() {
|
|
10
|
-
this.phase = 'thinking';
|
|
11
|
-
this.step = '';
|
|
12
|
-
this.toolTarget = '';
|
|
13
|
-
this.spinnerIndex = 0;
|
|
14
|
-
this.timer = null;
|
|
15
|
-
this.lastActivity = '';
|
|
16
|
-
this.lastToolTime = Date.now();
|
|
17
|
-
this.lastActivityTime = Date.now();
|
|
18
|
-
this.sessionNum = 0;
|
|
19
|
-
this.startTime = Date.now();
|
|
20
|
-
this.stallTimeoutMin = 30;
|
|
21
|
-
this.completionTimeoutMin = null;
|
|
22
|
-
this.toolRunning = false;
|
|
23
|
-
this.toolStartTime = 0;
|
|
24
|
-
this.currentToolName = '';
|
|
25
|
-
this._paused = false;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
start(sessionNum, stallTimeoutMin) {
|
|
29
|
-
this.sessionNum = sessionNum;
|
|
30
|
-
this.startTime = Date.now();
|
|
31
|
-
this.lastActivityTime = Date.now();
|
|
32
|
-
if (stallTimeoutMin > 0) this.stallTimeoutMin = stallTimeoutMin;
|
|
33
|
-
this.timer = setInterval(() => this._render(), 1000);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
stop() {
|
|
37
|
-
if (this.timer) {
|
|
38
|
-
clearInterval(this.timer);
|
|
39
|
-
this.timer = null;
|
|
40
|
-
}
|
|
41
|
-
process.stderr.write('\r\x1b[K');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
updatePhase(phase) {
|
|
45
|
-
this.phase = phase;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
updateStep(step) {
|
|
49
|
-
this.step = step;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
appendActivity(toolName, summary) {
|
|
53
|
-
this.lastActivity = `${toolName}: ${summary}`;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
setCompletionDetected(timeoutMin) {
|
|
57
|
-
this.completionTimeoutMin = timeoutMin;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
updateActivity() {
|
|
61
|
-
this.lastActivityTime = Date.now();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
startTool(name) {
|
|
65
|
-
this.toolRunning = true;
|
|
66
|
-
this.toolStartTime = Date.now();
|
|
67
|
-
this.currentToolName = name;
|
|
68
|
-
this.lastActivityTime = Date.now();
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
endTool() {
|
|
72
|
-
if (!this.toolRunning) return;
|
|
73
|
-
this.toolRunning = false;
|
|
74
|
-
this.lastActivityTime = Date.now();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
pauseRendering() { this._paused = true; }
|
|
78
|
-
resumeRendering() { this._paused = false; }
|
|
79
|
-
|
|
80
|
-
getStatusLine() {
|
|
81
|
-
const clock = localTimestamp();
|
|
82
|
-
const elapsed = Math.floor((Date.now() - this.startTime) / 1000);
|
|
83
|
-
const mm = String(Math.floor(elapsed / 60)).padStart(2, '0');
|
|
84
|
-
const ss = String(elapsed % 60).padStart(2, '0');
|
|
85
|
-
const spinner = SPINNERS[this.spinnerIndex % SPINNERS.length];
|
|
86
|
-
|
|
87
|
-
const phaseLabel = this.phase === 'thinking'
|
|
88
|
-
? `${COLOR.yellow}思考中${COLOR.reset}`
|
|
89
|
-
: `${COLOR.green}编码中${COLOR.reset}`;
|
|
90
|
-
|
|
91
|
-
const idleMs = Date.now() - this.lastActivityTime;
|
|
92
|
-
const idleMin = Math.floor(idleMs / 60000);
|
|
93
|
-
|
|
94
|
-
let line = `${spinner} [Session ${this.sessionNum}] ${clock} ${phaseLabel} ${mm}:${ss}`;
|
|
95
|
-
if (idleMin >= 2) {
|
|
96
|
-
if (this.toolRunning) {
|
|
97
|
-
const toolSec = Math.floor((Date.now() - this.toolStartTime) / 1000);
|
|
98
|
-
const toolMm = Math.floor(toolSec / 60);
|
|
99
|
-
const toolSs = toolSec % 60;
|
|
100
|
-
line += ` | ${COLOR.yellow}工具执行中 ${toolMm}:${String(toolSs).padStart(2, '0')}${COLOR.reset}`;
|
|
101
|
-
} else if (this.completionTimeoutMin) {
|
|
102
|
-
line += ` | ${COLOR.red}${idleMin}分无响应(session_result 已写入, ${this.completionTimeoutMin}分钟超时自动中断)${COLOR.reset}`;
|
|
103
|
-
} else {
|
|
104
|
-
line += ` | ${COLOR.red}${idleMin}分无响应(等待模型响应, ${this.stallTimeoutMin}分钟超时自动中断)${COLOR.reset}`;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (this.step) {
|
|
108
|
-
line += ` | ${this.step}`;
|
|
109
|
-
if (this.toolTarget) {
|
|
110
|
-
// 动态获取终端宽度,默认 120 适配现代终端
|
|
111
|
-
const cols = process.stderr.columns || 120;
|
|
112
|
-
const usedWidth = line.replace(/\x1b\[[^m]*m/g, '').length;
|
|
113
|
-
const availWidth = Math.max(20, cols - usedWidth - 4);
|
|
114
|
-
const target = truncatePath(this.toolTarget, availWidth);
|
|
115
|
-
line += `: ${target}`;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
return line;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
_render() {
|
|
122
|
-
if (this._paused) return;
|
|
123
|
-
this.spinnerIndex++;
|
|
124
|
-
process.stderr.write(`\r\x1b[K${this.getStatusLine()}`);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function extractFileTarget(toolInput) {
|
|
129
|
-
const raw = typeof toolInput === 'object'
|
|
130
|
-
? (toolInput.file_path || toolInput.path || '')
|
|
131
|
-
: '';
|
|
132
|
-
if (!raw) return '';
|
|
133
|
-
return raw.split('/').slice(-2).join('/');
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function extractBashLabel(cmd) {
|
|
137
|
-
if (cmd.includes('git ')) return 'Git 操作';
|
|
138
|
-
if (cmd.includes('npm ') || cmd.includes('pip ') || cmd.includes('pnpm ') || cmd.includes('yarn ')) return '安装依赖';
|
|
139
|
-
if (/\b(sleep|Start-Sleep|timeout\s+\/t)\b/i.test(cmd)) return '等待就绪';
|
|
140
|
-
if (cmd.includes('curl')) return '网络请求';
|
|
141
|
-
if (cmd.includes('pytest') || cmd.includes('jest') || /\btest\b/.test(cmd)) return '测试验证';
|
|
142
|
-
if (cmd.includes('python ') || cmd.includes('node ')) return '执行脚本';
|
|
143
|
-
return '执行命令';
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function extractMcpTarget(toolInput) {
|
|
147
|
-
if (!toolInput || typeof toolInput !== 'object') return '';
|
|
148
|
-
return String(toolInput.url || toolInput.text || toolInput.element || '').slice(0, 60);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* 提取 Bash 命令的主体部分(移除管道、重定向等)
|
|
153
|
-
* 正确处理引号内的内容,不会错误分割引号内的分隔符
|
|
154
|
-
*/
|
|
155
|
-
function extractBashTarget(cmd) {
|
|
156
|
-
// 移除开头的 cd xxx && 部分
|
|
157
|
-
let clean = cmd.replace(/^(?:cd\s+\S+\s*&&\s*)+/g, '').trim();
|
|
158
|
-
|
|
159
|
-
// 临时替换引号内的分隔符为占位符
|
|
160
|
-
const unescape = (s) => s.replace(/\x00/g, ';');
|
|
161
|
-
clean = clean.replace(/"[^"]*"/g, m => m.replace(/[;|&]/g, '\x00'));
|
|
162
|
-
clean = clean.replace(/'[^']*'/g, m => m.replace(/[;|&]/g, '\x00'));
|
|
163
|
-
|
|
164
|
-
// 分割并取第一部分
|
|
165
|
-
clean = clean.split(/\s*(?:\|\|?|;|&&|2>&1|2>\/dev\/null|>\s*\/dev\/null)\s*/)[0];
|
|
166
|
-
|
|
167
|
-
// 还原占位符
|
|
168
|
-
return unescape(clean).trim();
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function inferPhaseStep(indicator, toolName, toolInput) {
|
|
172
|
-
const name = (toolName || '').toLowerCase();
|
|
173
|
-
|
|
174
|
-
indicator.startTool(toolName);
|
|
175
|
-
|
|
176
|
-
if (name === 'write' || name === 'edit' || name === 'multiedit' || name === 'str_replace_editor' || name === 'strreplace') {
|
|
177
|
-
indicator.updatePhase('coding');
|
|
178
|
-
indicator.updateStep('编辑文件');
|
|
179
|
-
indicator.toolTarget = extractFileTarget(toolInput);
|
|
180
|
-
} else if (name === 'bash' || name === 'shell') {
|
|
181
|
-
const cmd = typeof toolInput === 'object' ? (toolInput.command || '') : String(toolInput || '');
|
|
182
|
-
const label = extractBashLabel(cmd);
|
|
183
|
-
indicator.updateStep(label);
|
|
184
|
-
indicator.toolTarget = extractBashTarget(cmd);
|
|
185
|
-
if (label === '测试验证' || label === '执行脚本' || label === '执行命令') {
|
|
186
|
-
indicator.updatePhase('coding');
|
|
187
|
-
}
|
|
188
|
-
} else if (name === 'read' || name === 'glob' || name === 'grep' || name === 'ls') {
|
|
189
|
-
indicator.updatePhase('thinking');
|
|
190
|
-
indicator.updateStep('读取文件');
|
|
191
|
-
indicator.toolTarget = extractFileTarget(toolInput);
|
|
192
|
-
} else if (name === 'task') {
|
|
193
|
-
indicator.updatePhase('thinking');
|
|
194
|
-
indicator.updateStep('子 Agent 搜索');
|
|
195
|
-
indicator.toolTarget = '';
|
|
196
|
-
} else if (name === 'websearch' || name === 'webfetch') {
|
|
197
|
-
indicator.updatePhase('thinking');
|
|
198
|
-
indicator.updateStep('查阅文档');
|
|
199
|
-
indicator.toolTarget = '';
|
|
200
|
-
} else if (name.startsWith('mcp__')) {
|
|
201
|
-
indicator.updatePhase('coding');
|
|
202
|
-
const action = name.split('__').pop() || name;
|
|
203
|
-
indicator.updateStep(`浏览器: ${action}`);
|
|
204
|
-
indicator.toolTarget = extractMcpTarget(toolInput);
|
|
205
|
-
} else {
|
|
206
|
-
indicator.updateStep('工具调用');
|
|
207
|
-
indicator.toolTarget = '';
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
let summary;
|
|
211
|
-
if (typeof toolInput === 'object') {
|
|
212
|
-
const target = toolInput.file_path || toolInput.path || '';
|
|
213
|
-
const cmd = toolInput.command || '';
|
|
214
|
-
const pattern = toolInput.pattern || '';
|
|
215
|
-
summary = target || (cmd ? cmd.slice(0, 200) : '') || (pattern ? `pattern: ${pattern}` : JSON.stringify(toolInput).slice(0, 200));
|
|
216
|
-
} else {
|
|
217
|
-
summary = String(toolInput || '').slice(0, 200);
|
|
218
|
-
}
|
|
219
|
-
indicator.appendActivity(toolName, summary);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
module.exports = { Indicator, inferPhaseStep };
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { COLOR } = require('./config');
|
|
4
|
+
const { localTimestamp, truncatePath } = require('./utils');
|
|
5
|
+
|
|
6
|
+
const SPINNERS = ['⠋', '⠙', '⠸', '⠴', '⠦', '⠇'];
|
|
7
|
+
|
|
8
|
+
class Indicator {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.phase = 'thinking';
|
|
11
|
+
this.step = '';
|
|
12
|
+
this.toolTarget = '';
|
|
13
|
+
this.spinnerIndex = 0;
|
|
14
|
+
this.timer = null;
|
|
15
|
+
this.lastActivity = '';
|
|
16
|
+
this.lastToolTime = Date.now();
|
|
17
|
+
this.lastActivityTime = Date.now();
|
|
18
|
+
this.sessionNum = 0;
|
|
19
|
+
this.startTime = Date.now();
|
|
20
|
+
this.stallTimeoutMin = 30;
|
|
21
|
+
this.completionTimeoutMin = null;
|
|
22
|
+
this.toolRunning = false;
|
|
23
|
+
this.toolStartTime = 0;
|
|
24
|
+
this.currentToolName = '';
|
|
25
|
+
this._paused = false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
start(sessionNum, stallTimeoutMin) {
|
|
29
|
+
this.sessionNum = sessionNum;
|
|
30
|
+
this.startTime = Date.now();
|
|
31
|
+
this.lastActivityTime = Date.now();
|
|
32
|
+
if (stallTimeoutMin > 0) this.stallTimeoutMin = stallTimeoutMin;
|
|
33
|
+
this.timer = setInterval(() => this._render(), 1000);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
stop() {
|
|
37
|
+
if (this.timer) {
|
|
38
|
+
clearInterval(this.timer);
|
|
39
|
+
this.timer = null;
|
|
40
|
+
}
|
|
41
|
+
process.stderr.write('\r\x1b[K');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
updatePhase(phase) {
|
|
45
|
+
this.phase = phase;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
updateStep(step) {
|
|
49
|
+
this.step = step;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
appendActivity(toolName, summary) {
|
|
53
|
+
this.lastActivity = `${toolName}: ${summary}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
setCompletionDetected(timeoutMin) {
|
|
57
|
+
this.completionTimeoutMin = timeoutMin;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
updateActivity() {
|
|
61
|
+
this.lastActivityTime = Date.now();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
startTool(name) {
|
|
65
|
+
this.toolRunning = true;
|
|
66
|
+
this.toolStartTime = Date.now();
|
|
67
|
+
this.currentToolName = name;
|
|
68
|
+
this.lastActivityTime = Date.now();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
endTool() {
|
|
72
|
+
if (!this.toolRunning) return;
|
|
73
|
+
this.toolRunning = false;
|
|
74
|
+
this.lastActivityTime = Date.now();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
pauseRendering() { this._paused = true; }
|
|
78
|
+
resumeRendering() { this._paused = false; }
|
|
79
|
+
|
|
80
|
+
getStatusLine() {
|
|
81
|
+
const clock = localTimestamp();
|
|
82
|
+
const elapsed = Math.floor((Date.now() - this.startTime) / 1000);
|
|
83
|
+
const mm = String(Math.floor(elapsed / 60)).padStart(2, '0');
|
|
84
|
+
const ss = String(elapsed % 60).padStart(2, '0');
|
|
85
|
+
const spinner = SPINNERS[this.spinnerIndex % SPINNERS.length];
|
|
86
|
+
|
|
87
|
+
const phaseLabel = this.phase === 'thinking'
|
|
88
|
+
? `${COLOR.yellow}思考中${COLOR.reset}`
|
|
89
|
+
: `${COLOR.green}编码中${COLOR.reset}`;
|
|
90
|
+
|
|
91
|
+
const idleMs = Date.now() - this.lastActivityTime;
|
|
92
|
+
const idleMin = Math.floor(idleMs / 60000);
|
|
93
|
+
|
|
94
|
+
let line = `${spinner} [Session ${this.sessionNum}] ${clock} ${phaseLabel} ${mm}:${ss}`;
|
|
95
|
+
if (idleMin >= 2) {
|
|
96
|
+
if (this.toolRunning) {
|
|
97
|
+
const toolSec = Math.floor((Date.now() - this.toolStartTime) / 1000);
|
|
98
|
+
const toolMm = Math.floor(toolSec / 60);
|
|
99
|
+
const toolSs = toolSec % 60;
|
|
100
|
+
line += ` | ${COLOR.yellow}工具执行中 ${toolMm}:${String(toolSs).padStart(2, '0')}${COLOR.reset}`;
|
|
101
|
+
} else if (this.completionTimeoutMin) {
|
|
102
|
+
line += ` | ${COLOR.red}${idleMin}分无响应(session_result 已写入, ${this.completionTimeoutMin}分钟超时自动中断)${COLOR.reset}`;
|
|
103
|
+
} else {
|
|
104
|
+
line += ` | ${COLOR.red}${idleMin}分无响应(等待模型响应, ${this.stallTimeoutMin}分钟超时自动中断)${COLOR.reset}`;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (this.step) {
|
|
108
|
+
line += ` | ${this.step}`;
|
|
109
|
+
if (this.toolTarget) {
|
|
110
|
+
// 动态获取终端宽度,默认 120 适配现代终端
|
|
111
|
+
const cols = process.stderr.columns || 120;
|
|
112
|
+
const usedWidth = line.replace(/\x1b\[[^m]*m/g, '').length;
|
|
113
|
+
const availWidth = Math.max(20, cols - usedWidth - 4);
|
|
114
|
+
const target = truncatePath(this.toolTarget, availWidth);
|
|
115
|
+
line += `: ${target}`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return line;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
_render() {
|
|
122
|
+
if (this._paused) return;
|
|
123
|
+
this.spinnerIndex++;
|
|
124
|
+
process.stderr.write(`\r\x1b[K${this.getStatusLine()}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function extractFileTarget(toolInput) {
|
|
129
|
+
const raw = typeof toolInput === 'object'
|
|
130
|
+
? (toolInput.file_path || toolInput.path || '')
|
|
131
|
+
: '';
|
|
132
|
+
if (!raw) return '';
|
|
133
|
+
return raw.split('/').slice(-2).join('/');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function extractBashLabel(cmd) {
|
|
137
|
+
if (cmd.includes('git ')) return 'Git 操作';
|
|
138
|
+
if (cmd.includes('npm ') || cmd.includes('pip ') || cmd.includes('pnpm ') || cmd.includes('yarn ')) return '安装依赖';
|
|
139
|
+
if (/\b(sleep|Start-Sleep|timeout\s+\/t)\b/i.test(cmd)) return '等待就绪';
|
|
140
|
+
if (cmd.includes('curl')) return '网络请求';
|
|
141
|
+
if (cmd.includes('pytest') || cmd.includes('jest') || /\btest\b/.test(cmd)) return '测试验证';
|
|
142
|
+
if (cmd.includes('python ') || cmd.includes('node ')) return '执行脚本';
|
|
143
|
+
return '执行命令';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function extractMcpTarget(toolInput) {
|
|
147
|
+
if (!toolInput || typeof toolInput !== 'object') return '';
|
|
148
|
+
return String(toolInput.url || toolInput.text || toolInput.element || '').slice(0, 60);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 提取 Bash 命令的主体部分(移除管道、重定向等)
|
|
153
|
+
* 正确处理引号内的内容,不会错误分割引号内的分隔符
|
|
154
|
+
*/
|
|
155
|
+
function extractBashTarget(cmd) {
|
|
156
|
+
// 移除开头的 cd xxx && 部分
|
|
157
|
+
let clean = cmd.replace(/^(?:cd\s+\S+\s*&&\s*)+/g, '').trim();
|
|
158
|
+
|
|
159
|
+
// 临时替换引号内的分隔符为占位符
|
|
160
|
+
const unescape = (s) => s.replace(/\x00/g, ';');
|
|
161
|
+
clean = clean.replace(/"[^"]*"/g, m => m.replace(/[;|&]/g, '\x00'));
|
|
162
|
+
clean = clean.replace(/'[^']*'/g, m => m.replace(/[;|&]/g, '\x00'));
|
|
163
|
+
|
|
164
|
+
// 分割并取第一部分
|
|
165
|
+
clean = clean.split(/\s*(?:\|\|?|;|&&|2>&1|2>\/dev\/null|>\s*\/dev\/null)\s*/)[0];
|
|
166
|
+
|
|
167
|
+
// 还原占位符
|
|
168
|
+
return unescape(clean).trim();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function inferPhaseStep(indicator, toolName, toolInput) {
|
|
172
|
+
const name = (toolName || '').toLowerCase();
|
|
173
|
+
|
|
174
|
+
indicator.startTool(toolName);
|
|
175
|
+
|
|
176
|
+
if (name === 'write' || name === 'edit' || name === 'multiedit' || name === 'str_replace_editor' || name === 'strreplace') {
|
|
177
|
+
indicator.updatePhase('coding');
|
|
178
|
+
indicator.updateStep('编辑文件');
|
|
179
|
+
indicator.toolTarget = extractFileTarget(toolInput);
|
|
180
|
+
} else if (name === 'bash' || name === 'shell') {
|
|
181
|
+
const cmd = typeof toolInput === 'object' ? (toolInput.command || '') : String(toolInput || '');
|
|
182
|
+
const label = extractBashLabel(cmd);
|
|
183
|
+
indicator.updateStep(label);
|
|
184
|
+
indicator.toolTarget = extractBashTarget(cmd);
|
|
185
|
+
if (label === '测试验证' || label === '执行脚本' || label === '执行命令') {
|
|
186
|
+
indicator.updatePhase('coding');
|
|
187
|
+
}
|
|
188
|
+
} else if (name === 'read' || name === 'glob' || name === 'grep' || name === 'ls') {
|
|
189
|
+
indicator.updatePhase('thinking');
|
|
190
|
+
indicator.updateStep('读取文件');
|
|
191
|
+
indicator.toolTarget = extractFileTarget(toolInput);
|
|
192
|
+
} else if (name === 'task') {
|
|
193
|
+
indicator.updatePhase('thinking');
|
|
194
|
+
indicator.updateStep('子 Agent 搜索');
|
|
195
|
+
indicator.toolTarget = '';
|
|
196
|
+
} else if (name === 'websearch' || name === 'webfetch') {
|
|
197
|
+
indicator.updatePhase('thinking');
|
|
198
|
+
indicator.updateStep('查阅文档');
|
|
199
|
+
indicator.toolTarget = '';
|
|
200
|
+
} else if (name.startsWith('mcp__')) {
|
|
201
|
+
indicator.updatePhase('coding');
|
|
202
|
+
const action = name.split('__').pop() || name;
|
|
203
|
+
indicator.updateStep(`浏览器: ${action}`);
|
|
204
|
+
indicator.toolTarget = extractMcpTarget(toolInput);
|
|
205
|
+
} else {
|
|
206
|
+
indicator.updateStep('工具调用');
|
|
207
|
+
indicator.toolTarget = '';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
let summary;
|
|
211
|
+
if (typeof toolInput === 'object') {
|
|
212
|
+
const target = toolInput.file_path || toolInput.path || '';
|
|
213
|
+
const cmd = toolInput.command || '';
|
|
214
|
+
const pattern = toolInput.pattern || '';
|
|
215
|
+
summary = target || (cmd ? cmd.slice(0, 200) : '') || (pattern ? `pattern: ${pattern}` : JSON.stringify(toolInput).slice(0, 200));
|
|
216
|
+
} else {
|
|
217
|
+
summary = String(toolInput || '').slice(0, 200);
|
|
218
|
+
}
|
|
219
|
+
indicator.appendActivity(toolName, summary);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = { Indicator, inferPhaseStep };
|