winter-super-cli 2026.5.16 → 2026.5.18

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.5.16",
3
+ "version": "2026.5.18",
4
4
  "description": "❄️ AI-Powered Development CLI with Interactive REPL",
5
5
  "type": "module",
6
6
  "main": "bin/winter.js",
@@ -5,11 +5,12 @@
5
5
 
6
6
  import { promises as fs } from 'fs';
7
7
  import path from 'path';
8
- import { exec } from 'child_process';
8
+ import { exec, execFile } from 'child_process';
9
9
  import { promisify } from 'util';
10
10
  import { diffLines } from 'diff';
11
11
 
12
12
  const execAsync = promisify(exec);
13
+ const execFileAsync = promisify(execFile);
13
14
 
14
15
  export class ToolExecutor {
15
16
  constructor(repl) {
@@ -244,26 +245,53 @@ export class ToolExecutor {
244
245
  const aliases = {
245
246
  read: 'Read',
246
247
  readfile: 'Read',
248
+ openfile: 'Read',
249
+ viewfile: 'Read',
250
+ cat: 'Read',
247
251
  write: 'Write',
248
252
  writefile: 'Write',
253
+ writetofile: 'Write',
254
+ createfile: 'Write',
255
+ savefile: 'Write',
249
256
  edit: 'Edit',
250
257
  editfile: 'Edit',
258
+ replaceinfile: 'Edit',
259
+ strreplace: 'Edit',
260
+ strreplaceeditor: 'Edit',
261
+ applydiff: 'Edit',
262
+ patch: 'Edit',
251
263
  bash: 'Bash',
252
264
  shell: 'Bash',
253
265
  command: 'Bash',
266
+ commandexecutor: 'Bash',
267
+ executecommand: 'Bash',
268
+ runcommand: 'Bash',
269
+ terminal: 'Bash',
270
+ powershell: 'Bash',
254
271
  glob: 'Glob',
272
+ listfiles: 'Glob',
273
+ ls: 'Glob',
274
+ findfiles: 'Glob',
255
275
  grep: 'Grep',
256
276
  search: 'Grep',
277
+ searchfiles: 'Grep',
278
+ searchtext: 'Grep',
279
+ rg: 'Grep',
257
280
  lsp: 'LSP',
281
+ listcodedefinitionnames: 'LSP',
258
282
  taskcreate: 'TaskCreate',
259
283
  createtask: 'TaskCreate',
284
+ newtask: 'TaskCreate',
260
285
  taskupdate: 'TaskUpdate',
261
286
  updatetask: 'TaskUpdate',
262
287
  tasklist: 'TaskList',
263
288
  listtasks: 'TaskList',
289
+ plan: 'TaskList',
264
290
  webfetch: 'WebFetch',
265
291
  fetch: 'WebFetch',
292
+ fetchurl: 'WebFetch',
266
293
  websearch: 'WebSearch',
294
+ searchweb: 'WebSearch',
267
295
  browserdebug: 'BrowserDebug',
268
296
  browser: 'BrowserDebug',
269
297
  };
@@ -419,6 +447,9 @@ export class ToolExecutor {
419
447
  timeout = parseInt(timeout, 10);
420
448
  if (isNaN(timeout) || timeout < 0) timeout = 60000;
421
449
 
450
+ const heredocResult = await this.tryHandleHeredocWrite(command, cwd);
451
+ if (heredocResult) return heredocResult;
452
+
422
453
  command = await this.translateWindowsCommand(command);
423
454
 
424
455
  // Security check
@@ -430,7 +461,14 @@ export class ToolExecutor {
430
461
  }
431
462
 
432
463
  try {
433
- const { stdout, stderr } = await execAsync(command, { cwd, timeout, shell: true });
464
+ const { stdout, stderr } = process.platform === 'win32'
465
+ ? await execFileAsync('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', command], {
466
+ cwd,
467
+ timeout,
468
+ windowsHide: true,
469
+ maxBuffer: 10 * 1024 * 1024,
470
+ })
471
+ : await execAsync(command, { cwd, timeout, shell: true, maxBuffer: 10 * 1024 * 1024 });
434
472
  return {
435
473
  success: true,
436
474
  stdout: stdout || '',
@@ -531,16 +569,17 @@ export class ToolExecutor {
531
569
  if (process.platform !== 'win32') return command;
532
570
 
533
571
  const trimmed = command.trim();
534
- const lsRecursive = trimmed.match(/^ls\s+-R\s+(.+)$/i);
535
- if (lsRecursive) {
536
- const target = lsRecursive[1].trim();
537
- return `powershell -NoProfile -Command "Get-ChildItem -LiteralPath ${this.quotePowerShellString(target)} -Recurse -Force | Select-Object -ExpandProperty FullName"`;
538
- }
539
-
540
- const ls = trimmed.match(/^ls\s+(.+)$/i);
572
+ const ls = trimmed.match(/^ls(?:\s+(.+))?$/i);
541
573
  if (ls) {
542
- const target = ls[1].trim();
543
- return `powershell -NoProfile -Command "Get-ChildItem -LiteralPath ${this.quotePowerShellString(target)} -Force | Select-Object -ExpandProperty Name"`;
574
+ const parsed = this.parseLsArgs(ls[1] || '');
575
+ const target = parsed.target || '.';
576
+ const recurse = parsed.flags.has('r') || parsed.flags.has('recursive');
577
+ const long = parsed.flags.has('l') || parsed.flags.has('la') || parsed.flags.has('al');
578
+ const force = parsed.flags.has('a') || parsed.flags.has('force') || parsed.flags.has('la') || parsed.flags.has('al');
579
+ const fields = long
580
+ ? 'Mode,Length,LastWriteTime,Name'
581
+ : 'Name';
582
+ return `Get-ChildItem -LiteralPath ${this.quotePowerShellString(target)}${recurse ? ' -Recurse' : ''}${force ? ' -Force' : ''} | Select-Object ${fields}`;
544
583
  }
545
584
 
546
585
  const cat = trimmed.match(/^cat\s+(.+)$/i);
@@ -550,15 +589,82 @@ export class ToolExecutor {
550
589
  try {
551
590
  const stat = await fs.stat(normalizedTarget);
552
591
  if (stat.isDirectory()) {
553
- return `powershell -NoProfile -Command "Get-ChildItem -LiteralPath ${this.quotePowerShellString(target)} -Force | Select-Object -ExpandProperty Name"`;
592
+ return `Get-ChildItem -LiteralPath ${this.quotePowerShellString(target)} -Force | Select-Object -ExpandProperty Name`;
554
593
  }
555
594
  } catch {}
556
- return `powershell -NoProfile -Command "Get-Content -LiteralPath ${this.quotePowerShellString(target)}"`;
595
+ return `Get-Content -LiteralPath ${this.quotePowerShellString(target)}`;
557
596
  }
558
597
 
559
598
  return command;
560
599
  }
561
600
 
601
+ async tryHandleHeredocWrite(command, cwd) {
602
+ const match = String(command || '').match(/^cat\s*>\s*(.+?)\s*<<\s*['"]?([A-Za-z0-9_-]+)['"]?\r?\n([\s\S]*?)\r?\n\2\s*$/);
603
+ if (!match) return null;
604
+
605
+ const [, rawTarget, , content] = match;
606
+ const target = this.resolveInputPath(rawTarget.trim(), cwd);
607
+ return await this.writeFile(target, content.endsWith('\n') ? content : `${content}\n`);
608
+ }
609
+
610
+ parseLsArgs(rawArgs) {
611
+ const tokens = this.splitShellLike(rawArgs);
612
+ const flags = new Set();
613
+ const paths = [];
614
+
615
+ for (const token of tokens) {
616
+ if (token.startsWith('-') && token.length > 1) {
617
+ const flag = token.replace(/^-+/, '').toLowerCase();
618
+ flags.add(flag);
619
+ if (!flag.includes('=')) {
620
+ for (const ch of flag) flags.add(ch);
621
+ }
622
+ } else {
623
+ paths.push(token);
624
+ }
625
+ }
626
+
627
+ return { flags, target: paths.join(' ') };
628
+ }
629
+
630
+ splitShellLike(text) {
631
+ const tokens = [];
632
+ let current = '';
633
+ let quote = null;
634
+ let escaped = false;
635
+
636
+ for (const ch of String(text || '')) {
637
+ if (escaped) {
638
+ current += ch;
639
+ escaped = false;
640
+ continue;
641
+ }
642
+ if (ch === '\\') {
643
+ escaped = true;
644
+ continue;
645
+ }
646
+ if ((ch === '"' || ch === "'") && !quote) {
647
+ quote = ch;
648
+ continue;
649
+ }
650
+ if (ch === quote) {
651
+ quote = null;
652
+ continue;
653
+ }
654
+ if (/\s/.test(ch) && !quote) {
655
+ if (current) {
656
+ tokens.push(current);
657
+ current = '';
658
+ }
659
+ continue;
660
+ }
661
+ current += ch;
662
+ }
663
+
664
+ if (current) tokens.push(current);
665
+ return tokens;
666
+ }
667
+
562
668
  quotePowerShellString(value) {
563
669
  const unquoted = value.replace(/^['"]|['"]$/g, '');
564
670
  return `'${unquoted.replace(/'/g, "''")}'`;
@@ -18,10 +18,50 @@ test('tool names accept common model aliases', () => {
18
18
  const tools = new ToolExecutor({ projectPath: process.cwd() });
19
19
 
20
20
  assert.equal(tools.normalizeToolName('read_file'), 'Read');
21
+ assert.equal(tools.normalizeToolName('write_to_file'), 'Write');
22
+ assert.equal(tools.normalizeToolName('replace_in_file'), 'Edit');
23
+ assert.equal(tools.normalizeToolName('command_executor'), 'Bash');
24
+ assert.equal(tools.normalizeToolName('execute_command'), 'Bash');
25
+ assert.equal(tools.normalizeToolName('list_files'), 'Glob');
26
+ assert.equal(tools.normalizeToolName('search_files'), 'Grep');
21
27
  assert.equal(tools.normalizeToolName('shell'), 'Bash');
22
28
  assert.equal(tools.normalizeToolName('web-search'), 'WebSearch');
23
29
  });
24
30
 
31
+ test('Bash supports model-style heredoc file writes', async () => {
32
+ const root = await mkdtemp(path.join(tmpdir(), 'winter-heredoc-'));
33
+ const tools = new ToolExecutor({ projectPath: root });
34
+ const result = await tools.execute('command_executor', {
35
+ command: "cat > src/components/InstallSection.tsx << 'EOF'\nconst ok = true;\nEOF",
36
+ });
37
+
38
+ assert.equal(result.success, true);
39
+ assert.equal(result.path, path.join(root, 'src', 'components', 'InstallSection.tsx'));
40
+
41
+ const read = await tools.execute('Read', { file_path: 'src/components/InstallSection.tsx' });
42
+ assert.equal(read.content, 'const ok = true;\n');
43
+ });
44
+
45
+ test('Windows Bash runs PowerShell commands and translates ls flags', async (t) => {
46
+ if (process.platform !== 'win32') {
47
+ t.skip('Windows-only shell behavior');
48
+ return;
49
+ }
50
+
51
+ const root = await mkdtemp(path.join(tmpdir(), 'winter-powershell-'));
52
+ await mkdir(path.join(root, 'src', 'components'), { recursive: true });
53
+ await writeFile(path.join(root, 'src', 'components', 'a.txt'), 'hello');
54
+
55
+ const tools = new ToolExecutor({ projectPath: root });
56
+ const ls = await tools.execute('Bash', { command: 'ls -la src/components/' });
57
+ assert.equal(ls.success, true);
58
+ assert.match(ls.stdout, /a\.txt/);
59
+
60
+ const ps = await tools.execute('Bash', { command: 'Get-ChildItem -Path src/components/ -Name' });
61
+ assert.equal(ps.success, true);
62
+ assert.match(ps.stdout, /a\.txt/);
63
+ });
64
+
25
65
  test('Read lists directories instead of failing on directory paths', async () => {
26
66
  const root = await mkdtemp(path.join(tmpdir(), 'winter-tools-'));
27
67
  await mkdir(path.join(root, 'sub'));