winter-super-cli 2026.6.27 → 2026.6.28
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/agent/runtime.js +27 -0
- package/src/cli/config.js +2 -2
- package/src/cli/repl-commands.js +8 -2
- package/src/cli/repl.js +36 -1
- package/src/tools/executor.js +5 -3
package/package.json
CHANGED
package/src/agent/runtime.js
CHANGED
|
@@ -37,6 +37,7 @@ export class AgentRuntime {
|
|
|
37
37
|
const executionProfile = repl.selectExecutionProfile(messages, { enableTools: true });
|
|
38
38
|
const requireToolEvidence = repl.actionRequiresTools(messages);
|
|
39
39
|
let noToolActionRetries = 0;
|
|
40
|
+
let unfinishedActionRetries = 0;
|
|
40
41
|
const sessionContext = repl.session?.getContext?.() || {};
|
|
41
42
|
const profile = sessionContext.workflowProfile || 'general';
|
|
42
43
|
const depth = /deep/i.test(profile) ? 'deep' : 'standard';
|
|
@@ -140,6 +141,32 @@ export class AgentRuntime {
|
|
|
140
141
|
reachedToolLimit = false;
|
|
141
142
|
break;
|
|
142
143
|
}
|
|
144
|
+
if (
|
|
145
|
+
turn.finalContent &&
|
|
146
|
+
requireToolEvidence &&
|
|
147
|
+
usedTools &&
|
|
148
|
+
!usedMutatingTools &&
|
|
149
|
+
repl.responseIndicatesUnfinishedAction?.(turn.finalContent)
|
|
150
|
+
) {
|
|
151
|
+
unfinishedActionRetries++;
|
|
152
|
+
if (unfinishedActionRetries > 3) {
|
|
153
|
+
finalContent = 'Chưa hoàn thành: model chỉ trả lời trạng thái sau khi inspect, chưa thực hiện thay đổi. Winter đã dừng để tránh báo tiến độ giả.';
|
|
154
|
+
console.log(`\n${colors.yellow}${finalContent}${colors.reset}\n`);
|
|
155
|
+
reachedToolLimit = false;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
messages.push({
|
|
159
|
+
role: 'assistant',
|
|
160
|
+
content: assistantMsg.content || '',
|
|
161
|
+
});
|
|
162
|
+
messages.push({
|
|
163
|
+
role: 'user',
|
|
164
|
+
content: repl.buildUnfinishedActionCorrection(messages, turn.finalContent),
|
|
165
|
+
});
|
|
166
|
+
forceTextToolFallback = true;
|
|
167
|
+
finalContent = '';
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
143
170
|
if (turn.finalContent) {
|
|
144
171
|
finalContent = turn.finalContent;
|
|
145
172
|
}
|
package/src/cli/config.js
CHANGED
|
@@ -121,11 +121,11 @@ export class ConfigLoader {
|
|
|
121
121
|
enabled: true,
|
|
122
122
|
restrictToWorkspace: true,
|
|
123
123
|
allowedCommands: [
|
|
124
|
-
'git', 'npm', 'npx', 'node', 'python',
|
|
124
|
+
'git', 'npm', 'npx', 'node', 'python', 'powershell', 'pwsh', 'cmd',
|
|
125
125
|
'ping', 'test-connection', 'curl', 'wget', 'iwr', 'irm',
|
|
126
126
|
'invoke-webrequest', 'invoke-restmethod', 'nslookup', 'resolve-dnsname',
|
|
127
127
|
'tracert', 'traceroute', 'pathping', 'dig', 'ipconfig', 'ifconfig',
|
|
128
|
-
'ip', 'netstat', 'speedtest', 'speedtest-cli', 'measure-command',
|
|
128
|
+
'ip', 'netstat', 'speedtest', 'speedtest-cli', 'measure-command', 'where',
|
|
129
129
|
],
|
|
130
130
|
},
|
|
131
131
|
session: {
|
package/src/cli/repl-commands.js
CHANGED
|
@@ -673,7 +673,6 @@ export async function handleSlashCommand(repl, input) {
|
|
|
673
673
|
break;
|
|
674
674
|
|
|
675
675
|
// Planning
|
|
676
|
-
case '/plan':
|
|
677
676
|
case '/plans':
|
|
678
677
|
const plans = repl.session.getPlans();
|
|
679
678
|
if (plans.length === 0) {
|
|
@@ -683,6 +682,14 @@ export async function handleSlashCommand(repl, input) {
|
|
|
683
682
|
plans.forEach(p => console.log(` [${p.status}] ${p.title}`));
|
|
684
683
|
}
|
|
685
684
|
break;
|
|
685
|
+
case '/plan':
|
|
686
|
+
if (args.length === 0) {
|
|
687
|
+
console.log(`${colors.yellow}Usage: /plan <task>${colors.reset}`);
|
|
688
|
+
console.log(`${colors.dim}Use /plans to list active plans.${colors.reset}`);
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
await repl.generateInteractivePlan(args.join(' '));
|
|
692
|
+
return;
|
|
686
693
|
case '/task':
|
|
687
694
|
case '/tasks':
|
|
688
695
|
console.log(`${colors.cyan}Tasks:${colors.reset}`);
|
|
@@ -848,7 +855,6 @@ export async function handleSlashCommand(repl, input) {
|
|
|
848
855
|
}
|
|
849
856
|
console.log(`${colors.yellow}Usage: /doctor [full|tools|context|scorecard]${colors.reset}`);
|
|
850
857
|
return;
|
|
851
|
-
case '/plan:':
|
|
852
858
|
case '/plan-gen':
|
|
853
859
|
if (args.length === 0) {
|
|
854
860
|
console.log(`${colors.yellow}Usage: /plan <task>${colors.reset}`);
|
package/src/cli/repl.js
CHANGED
|
@@ -1963,7 +1963,10 @@ ${colors.reset}
|
|
|
1963
1963
|
if (this.isBrowserInteractionRequest(rawText)) return true;
|
|
1964
1964
|
|
|
1965
1965
|
// Even without explicit target, some verbs are strong enough on their own
|
|
1966
|
-
const
|
|
1966
|
+
const continuationAction = /\b(continue|resume|start|begin|tiep|tiếp|bat dau|bắt đầu)\b/i;
|
|
1967
|
+
if (continuationAction.test(text)) return true;
|
|
1968
|
+
|
|
1969
|
+
const strongActionAlone = /\b(fix|debug|deploy|build|test|commit|install|run|refactor|start|begin|sửa|chạy|cài|triển khai|xây dựng|bắt đầu|bat dau)\b/i;
|
|
1967
1970
|
if (strongActionAlone.test(text)) return true;
|
|
1968
1971
|
|
|
1969
1972
|
return actionPattern.test(text) && targetPattern.test(text);
|
|
@@ -1990,6 +1993,38 @@ ${colors.reset}
|
|
|
1990
1993
|
return true;
|
|
1991
1994
|
}
|
|
1992
1995
|
|
|
1996
|
+
responseIndicatesUnfinishedAction(content = '') {
|
|
1997
|
+
const raw = String(content || '').trim();
|
|
1998
|
+
if (!raw) return false;
|
|
1999
|
+
const text = `${raw}\n${this.normalizeIntentText(raw)}`.toLowerCase();
|
|
2000
|
+
|
|
2001
|
+
const inProgress = /\b(đang|dang|sẽ|se|tiếp theo|tiep theo|next(?:,|\s+i|\s+step)?|i(?:'ll| will| am going to)|going to|để tôi|de toi|let me|cần (?:thêm|sửa|tạo|viết)|can (?:add|create|implement)|need to (?:add|create|implement|edit|write|fix)|thiếu .*?(?:endpoint|api|view|ui|frontend|backend|route|function)|missing .*?(?:endpoint|api|view|ui|frontend|backend|route|function))\b/i;
|
|
2002
|
+
const workVerb = /\b(làm|lam|sửa|sua|thêm|them|tạo|tao|viết|viet|cập nhật|cap nhat|implement|add|create|write|edit|update|patch|fix|build|wire|connect|endpoint|api|view|ui|frontend|backend)\b/i;
|
|
2003
|
+
const onlyInspection = /\b(đọc|doc|read|grep|search|inspect|kiểm tra|kiem tra|found|thấy|thay|xác định|xac dinh)\b/i;
|
|
2004
|
+
|
|
2005
|
+
if (inProgress.test(text) && (workVerb.test(text) || onlyInspection.test(text))) return true;
|
|
2006
|
+
if (/đang làm tiếp|dang lam tiep|doing next|working on it|đang sửa|dang sua|đang thêm|dang them/i.test(text)) return true;
|
|
2007
|
+
return false;
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
buildUnfinishedActionCorrection(messages = [], content = '') {
|
|
2011
|
+
const request = this.getLatestUserText(messages);
|
|
2012
|
+
return [
|
|
2013
|
+
'RUNTIME ENFORCEMENT: Your previous response was only progress/status, not completion.',
|
|
2014
|
+
'',
|
|
2015
|
+
'You already inspected, but the user asked you to continue/do the work. Keep going with tool calls now:',
|
|
2016
|
+
'1. If you identified missing backend/frontend pieces, call Read/Grep for the exact files still needed.',
|
|
2017
|
+
'2. Call Edit/Write/InsertText/StrReplaceAll to make the code changes.',
|
|
2018
|
+
'3. Call Bash to run the closest test/build/smoke check.',
|
|
2019
|
+
'4. Only then give the final answer.',
|
|
2020
|
+
'',
|
|
2021
|
+
'Do not answer with "đang làm", "sẽ thêm", "cần thêm", or a plan. Use tools.',
|
|
2022
|
+
'',
|
|
2023
|
+
`Original user request: ${request}`,
|
|
2024
|
+
content ? `Blocked progress response: ${String(content).slice(0, 1000)}` : '',
|
|
2025
|
+
].filter(Boolean).join('\n');
|
|
2026
|
+
}
|
|
2027
|
+
|
|
1993
2028
|
isBrowserInteractionRequest(text = '') {
|
|
1994
2029
|
const raw = String(text || '');
|
|
1995
2030
|
const normalized = this.normalizeIntentText(raw);
|
package/src/tools/executor.js
CHANGED
|
@@ -32,8 +32,9 @@ export class ToolExecutor {
|
|
|
32
32
|
this.projectPath = repl?.projectPath || process.cwd();
|
|
33
33
|
this.allowedCommands = [
|
|
34
34
|
'git', 'npm', 'npx', 'node', 'python', 'code', 'pnpm', 'yarn', 'bun', 'pip', 'cargo', 'rustc',
|
|
35
|
+
'powershell', 'pwsh', 'cmd',
|
|
35
36
|
'echo', 'printf', 'cat', 'ls', 'dir', 'type', 'copy', 'mkdir', 'get-childitem', 'set-content',
|
|
36
|
-
'get-content', 'test-path', 'get-date',
|
|
37
|
+
'get-content', 'test-path', 'get-date', 'where',
|
|
37
38
|
'ping', 'test-connection', 'curl', 'wget', 'iwr', 'irm', 'invoke-webrequest', 'invoke-restmethod',
|
|
38
39
|
'nslookup', 'resolve-dnsname', 'tracert', 'traceroute', 'pathping', 'dig', 'ipconfig', 'ifconfig',
|
|
39
40
|
'ip', 'netstat', 'speedtest', 'speedtest-cli', 'measure-command',
|
|
@@ -1620,10 +1621,11 @@ export class ToolExecutor {
|
|
|
1620
1621
|
async execWindowsCommand(command, cwd, timeout, requestedShell = 'auto') {
|
|
1621
1622
|
const shell = requestedShell === 'auto' ? this.detectWindowsShell(command) : requestedShell;
|
|
1622
1623
|
if (shell === 'cmd') {
|
|
1623
|
-
return await execFileAsync('cmd.exe', ['/d', '/
|
|
1624
|
+
return await execFileAsync('cmd.exe', ['/d', '/c', command], {
|
|
1624
1625
|
cwd,
|
|
1625
1626
|
timeout,
|
|
1626
1627
|
windowsHide: true,
|
|
1628
|
+
windowsVerbatimArguments: true,
|
|
1627
1629
|
maxBuffer: 10 * 1024 * 1024,
|
|
1628
1630
|
});
|
|
1629
1631
|
}
|
|
@@ -1649,7 +1651,7 @@ export class ToolExecutor {
|
|
|
1649
1651
|
|
|
1650
1652
|
looksLikeCmd(command) {
|
|
1651
1653
|
return /\s(&&|\|\|)\s/.test(command)
|
|
1652
|
-
|| /(^|[&]\s*)(dir|copy|xcopy|del|erase|move|ren|type|echo|set|if|for|mkdir|rmdir)\b/i.test(command)
|
|
1654
|
+
|| /(^|[&]\s*)(dir|copy|xcopy|del|erase|move|ren|type|echo|set|if|for|mkdir|rmdir|where)\b/i.test(command)
|
|
1653
1655
|
|| /^\s*@?echo\s+/i.test(command)
|
|
1654
1656
|
|| /(^|\s)(\/b|\/s|\/q|\/y)\b/i.test(command);
|
|
1655
1657
|
}
|