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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "winter-super-cli",
3
- "version": "2026.6.27",
3
+ "version": "2026.6.28",
4
4
  "description": "❄️ AI-Powered Development CLI with Interactive REPL",
5
5
  "type": "module",
6
6
  "main": "bin/winter.js",
@@ -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: {
@@ -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 strongActionAlone = /\b(fix|debug|deploy|build|test|commit|install|run|refactor|sửa|chạy|cài|triển khai|xây dựng)\b/i;
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);
@@ -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', '/s', '/c', command], {
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
  }