claude-coder 1.5.2 → 1.5.4
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 +1 -1
- package/src/indicator.js +36 -25
- package/src/session.js +8 -2
package/package.json
CHANGED
package/src/indicator.js
CHANGED
|
@@ -16,6 +16,7 @@ class Indicator {
|
|
|
16
16
|
this.lastToolTime = Date.now();
|
|
17
17
|
this.sessionNum = 0;
|
|
18
18
|
this.startTime = Date.now();
|
|
19
|
+
this._lastContentKey = '';
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
start(sessionNum) {
|
|
@@ -87,7 +88,6 @@ class Indicator {
|
|
|
87
88
|
_render() {
|
|
88
89
|
this.spinnerIndex++;
|
|
89
90
|
const line = this.getStatusLine();
|
|
90
|
-
|
|
91
91
|
const maxWidth = process.stderr.columns || 80;
|
|
92
92
|
const truncated = line.length > maxWidth + 20 ? line.slice(0, maxWidth + 20) : line;
|
|
93
93
|
|
|
@@ -95,41 +95,60 @@ class Indicator {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
function extractFileTarget(toolInput) {
|
|
99
|
+
const raw = typeof toolInput === 'object'
|
|
100
|
+
? (toolInput.file_path || toolInput.path || '')
|
|
101
|
+
: '';
|
|
102
|
+
if (!raw) return '';
|
|
103
|
+
return raw.split('/').slice(-2).join('/').slice(0, 40);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function extractBashLabel(cmd) {
|
|
107
|
+
if (cmd.includes('git ')) return 'Git 操作';
|
|
108
|
+
if (cmd.includes('npm ') || cmd.includes('pip ') || cmd.includes('pnpm ') || cmd.includes('yarn ')) return '安装依赖';
|
|
109
|
+
if (cmd.includes('curl') || cmd.includes('pytest') || cmd.includes('jest') || /\btest\b/.test(cmd)) return '测试验证';
|
|
110
|
+
if (cmd.includes('python ') || cmd.includes('node ')) return '执行脚本';
|
|
111
|
+
return '执行命令';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function extractBashTarget(cmd) {
|
|
115
|
+
let clean = cmd.replace(/^(?:cd\s+\S+\s*&&\s*)+/g, '').trim();
|
|
116
|
+
clean = clean.split(/\s*(?:\|{1,2}|;|&&|2>&1|>\s*\/dev\/null)\s*/)[0].trim();
|
|
117
|
+
return clean.slice(0, 40);
|
|
118
|
+
}
|
|
119
|
+
|
|
99
120
|
function inferPhaseStep(indicator, toolName, toolInput) {
|
|
100
121
|
const name = (toolName || '').toLowerCase();
|
|
101
122
|
|
|
102
123
|
indicator.lastToolTime = Date.now();
|
|
103
124
|
|
|
104
|
-
const rawTarget = typeof toolInput === 'object'
|
|
105
|
-
? (toolInput.file_path || toolInput.path || toolInput.command || toolInput.pattern || '')
|
|
106
|
-
: String(toolInput || '');
|
|
107
|
-
const shortTarget = rawTarget.split('/').slice(-2).join('/').slice(0, 40);
|
|
108
|
-
indicator.toolTarget = shortTarget;
|
|
109
|
-
|
|
110
125
|
if (name === 'write' || name === 'edit' || name === 'multiedit' || name === 'str_replace_editor' || name === 'strreplace') {
|
|
111
126
|
indicator.updatePhase('coding');
|
|
127
|
+
indicator.updateStep('编辑文件');
|
|
128
|
+
indicator.toolTarget = extractFileTarget(toolInput);
|
|
112
129
|
} else if (name === 'bash' || name === 'shell') {
|
|
113
130
|
const cmd = typeof toolInput === 'object' ? (toolInput.command || '') : String(toolInput || '');
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
} else if (cmd.includes('test') || cmd.includes('curl') || cmd.includes('pytest')) {
|
|
119
|
-
indicator.updateStep('测试验证');
|
|
120
|
-
indicator.updatePhase('coding');
|
|
121
|
-
} else {
|
|
131
|
+
const label = extractBashLabel(cmd);
|
|
132
|
+
indicator.updateStep(label);
|
|
133
|
+
indicator.toolTarget = extractBashTarget(cmd);
|
|
134
|
+
if (label === '测试验证' || label === '执行脚本' || label === '执行命令') {
|
|
122
135
|
indicator.updatePhase('coding');
|
|
123
136
|
}
|
|
124
137
|
} else if (name === 'read' || name === 'glob' || name === 'grep' || name === 'ls') {
|
|
125
138
|
indicator.updatePhase('thinking');
|
|
126
139
|
indicator.updateStep('读取文件');
|
|
140
|
+
indicator.toolTarget = extractFileTarget(toolInput);
|
|
127
141
|
} else if (name === 'task') {
|
|
128
142
|
indicator.updatePhase('thinking');
|
|
129
143
|
indicator.updateStep('子 Agent 搜索');
|
|
144
|
+
indicator.toolTarget = '';
|
|
130
145
|
} else if (name === 'websearch' || name === 'webfetch') {
|
|
131
146
|
indicator.updatePhase('thinking');
|
|
132
147
|
indicator.updateStep('查阅文档');
|
|
148
|
+
indicator.toolTarget = '';
|
|
149
|
+
} else {
|
|
150
|
+
indicator.updateStep('工具调用');
|
|
151
|
+
indicator.toolTarget = '';
|
|
133
152
|
}
|
|
134
153
|
|
|
135
154
|
let summary;
|
|
@@ -137,15 +156,7 @@ function inferPhaseStep(indicator, toolName, toolInput) {
|
|
|
137
156
|
const target = toolInput.file_path || toolInput.path || '';
|
|
138
157
|
const cmd = toolInput.command || '';
|
|
139
158
|
const pattern = toolInput.pattern || '';
|
|
140
|
-
|
|
141
|
-
summary = target;
|
|
142
|
-
} else if (cmd) {
|
|
143
|
-
summary = cmd.slice(0, 200);
|
|
144
|
-
} else if (pattern) {
|
|
145
|
-
summary = `pattern: ${pattern}`;
|
|
146
|
-
} else {
|
|
147
|
-
summary = JSON.stringify(toolInput).slice(0, 200);
|
|
148
|
-
}
|
|
159
|
+
summary = target || (cmd ? cmd.slice(0, 200) : '') || (pattern ? `pattern: ${pattern}` : JSON.stringify(toolInput).slice(0, 200));
|
|
149
160
|
} else {
|
|
150
161
|
summary = String(toolInput || '').slice(0, 200);
|
|
151
162
|
}
|
package/src/session.js
CHANGED
|
@@ -73,14 +73,20 @@ function writeSessionSeparator(logStream, sessionNum, label) {
|
|
|
73
73
|
logStream.write(`\n${sep}\n[Session ${sessionNum}] ${label} ${new Date().toISOString()}\n${sep}\n`);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
let _lastPrintedStatusKey = '';
|
|
77
|
+
|
|
76
78
|
function logMessage(message, logStream, indicator) {
|
|
77
79
|
if (message.type === 'assistant' && message.message?.content) {
|
|
78
80
|
for (const block of message.message.content) {
|
|
79
81
|
if (block.type === 'text' && block.text) {
|
|
80
82
|
if (indicator) {
|
|
81
|
-
const statusLine = indicator.getStatusLine();
|
|
82
83
|
process.stderr.write('\r\x1b[K');
|
|
83
|
-
|
|
84
|
+
const contentKey = `${indicator.phase}|${indicator.step}|${indicator.toolTarget}`;
|
|
85
|
+
if (contentKey !== _lastPrintedStatusKey) {
|
|
86
|
+
_lastPrintedStatusKey = contentKey;
|
|
87
|
+
const statusLine = indicator.getStatusLine();
|
|
88
|
+
if (statusLine) process.stderr.write(statusLine + '\n');
|
|
89
|
+
}
|
|
84
90
|
}
|
|
85
91
|
process.stdout.write(block.text);
|
|
86
92
|
if (logStream) logStream.write(block.text);
|