winter-super-cli 2026.6.14 → 2026.6.16

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.14",
3
+ "version": "2026.6.16",
4
4
  "description": "❄️ AI-Powered Development CLI with Interactive REPL",
5
5
  "type": "module",
6
6
  "main": "bin/winter.js",
@@ -614,11 +614,15 @@ export class AIProviderManager {
614
614
 
615
615
  const decoder = new TextDecoder();
616
616
  let buffer = '';
617
+ let rawText = '';
618
+ let yieldedAny = false;
617
619
 
618
620
  for await (const chunk of response.body) {
619
621
  if (timeout.resetTimer) timeout.resetTimer();
620
622
 
621
- buffer += decoder.decode(chunk, { stream: true });
623
+ const decoded = decoder.decode(chunk, { stream: true });
624
+ rawText += decoded;
625
+ buffer += decoded;
622
626
  const lines = buffer.split(/\r?\n/);
623
627
  buffer = lines.pop() || '';
624
628
 
@@ -638,6 +642,7 @@ export class AIProviderManager {
638
642
 
639
643
  const choice = data.choices?.[0] || {};
640
644
  const content = choice.delta?.content ?? choice.message?.content ?? choice.text ?? '';
645
+ yieldedAny = true;
641
646
  yield {
642
647
  content,
643
648
  usage: data.usage,
@@ -653,6 +658,7 @@ export class AIProviderManager {
653
658
  try {
654
659
  const data = JSON.parse(payload);
655
660
  const choice = data.choices?.[0] || {};
661
+ yieldedAny = true;
656
662
  yield {
657
663
  content: choice.delta?.content ?? choice.message?.content ?? choice.text ?? '',
658
664
  usage: data.usage,
@@ -661,6 +667,24 @@ export class AIProviderManager {
661
667
  } catch {}
662
668
  }
663
669
  }
670
+
671
+ if (!yieldedAny) {
672
+ try {
673
+ const data = JSON.parse(rawText.trim());
674
+ const choice = data.choices?.[0] || {};
675
+ const content = choice.delta?.content ?? choice.message?.content ?? choice.text ?? '';
676
+ if (content || data.usage || choice.finish_reason) {
677
+ yield {
678
+ content,
679
+ usage: data.usage,
680
+ raw: data,
681
+ };
682
+ }
683
+ } catch {
684
+ // Some OpenAI-compatible servers return 200 with a non-SSE/non-JSON body
685
+ // when streaming is requested. Let the caller fall back to non-streaming.
686
+ }
687
+ }
664
688
  } catch (error) {
665
689
  throw normalizeFetchError(error, provider, timeoutMs, true, timeout.timedOut());
666
690
  } finally {
package/src/cli/repl.js CHANGED
@@ -662,8 +662,13 @@ export class WinterREPL {
662
662
  const PASTE_DELAY = 80;
663
663
 
664
664
  process.stdin.on('data', (chunk) => {
665
- // If a large chunk or chunk with newlines arrives, it's definitely a paste.
666
- if (chunk.length > 3 || chunk.includes('\n')) {
665
+ const text = chunk.toString('utf8');
666
+ const newlineCount = (text.match(/\r?\n/g) || []).length;
667
+
668
+ // A normal submitted line can arrive as one chunk like "hello\n".
669
+ // Treat only real multi-line chunks as paste, otherwise REPL input
670
+ // gets stuck in multiline mode and never reaches the AI.
671
+ if (newlineCount > 1) {
667
672
  this._isPasteChunk = true;
668
673
  if (this._pasteChunkTimer) clearTimeout(this._pasteChunkTimer);
669
674
  this._pasteChunkTimer = setTimeout(() => {
@@ -676,7 +681,7 @@ export class WinterREPL {
676
681
  this._pasteTimer = null;
677
682
  if (this._pasteBuffer.length === 0) return;
678
683
 
679
- const isSingleLineInput = this._pasteBuffer.length === 1 && !this._isPasteChunk;
684
+ const isSingleLineInput = this._pasteBuffer.length === 1 && this._multilineBuffer.length === 0;
680
685
  const isJustEmptyEnter = this._pasteBuffer.length === 1 && this._pasteBuffer[0].trim() === '';
681
686
 
682
687
  // Normal single-line submit