winter-super-cli 2026.6.11 → 2026.6.13

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/README.md CHANGED
@@ -2,17 +2,16 @@
2
2
 
3
3
  > **Winter** is a powerful, AI-driven CLI coding assistant designed to supercharge your development workflow directly from the terminal. Inspired by Claude Code and Codex CLI, Winter brings a beautiful Cyberpunk aesthetic, smart session management, autonomous agent capabilities, and 25+ AI tools to your local environment.
4
4
  >
5
- > *Winter la mot tro ly lap trinh CLI manh me duoc dieu khien boi AI, giup tang toc quy trinh phat trien truc tiep tu terminal. Lay cam hung tu Claude Code va Codex CLI, Winter mang den giao dien Cyberpunk dep mat, quan ly phien lam viec thong minh, kha nang agent tu tri, va hon 25 cong cu AI.*
5
+ > *Winter một trợ lập trình CLI mạnh mẽ được điều khiển bởi AI, giúp tăng tốc quy trình phát triển trực tiếp từ terminal. Lấy cảm hứng từ Claude Code Codex CLI, Winter mang đến giao diện Cyberpunk đẹp mắt, quản phiên làm việc thông minh, khả năng agent tự trị, hơn 25 công cụ AI.*
6
6
 
7
7
  ---
8
8
 
9
9
  ## Key Features
10
10
 
11
11
  | Feature | Description |
12
- |---|---|
12
+ |---------|-------------|
13
13
  | **26 AI Tools** | Read, Write, Edit, Bash, Glob, Grep, Notebook, Todo, Scheduler, Agent, MCP & more |
14
14
  | **Multi-Provider** | Anthropic, OpenAI, Ollama, Groq -- with **smart context routing** |
15
- | **26 Tools** | Full parity with Claude Code + extra tools (WebArchive, HtmlEffectiveness, Notebook, Todo, Scheduler, InsertText, StrReplaceAll) |
16
15
  | **Context Router** | Auto-selects best provider per task (Claude for code, OpenAI for docs, Groq for speed, Ollama for privacy) |
17
16
  | **Session Management** | Isolated sessions with persistent memory, plans, and context |
18
17
  | **MCP Support** | Model Context Protocol -- connect VS Code, GitHub, databases & more |
@@ -30,20 +29,22 @@
30
29
  | **Clipboard Integration** | `/paste` command reads clipboard directly |
31
30
  | **Image Support** | Analyze images & screenshots via `/image` |
32
31
  | **Cross-Platform** | Windows, macOS, Linux -- full shell support |
32
+ | **Codebase Index** | Fast semantic search with optional CodeGraph integration |
33
+ | **Embedded Context Corpus** | Built-in knowledge base from 7,900+ embedded documents |
33
34
 
34
35
  ---
35
36
 
36
37
  ## Project Stats
37
38
 
38
39
  | Metric | Value |
39
- |---|---|
40
- | **Total Lines of Code** | ~13,500 |
41
- | **Source Files** | 40+ |
40
+ |--------|-------|
41
+ | **Total Source Files** | 125 |
42
+ | **Source Lines of Code** | 26,080 |
43
+ | **Source Code Size** | ~1 MB |
42
44
  | **Tools** | **26** |
43
- | **Tests** | **213** (all passing) |
44
- | **Exported Symbols** | 78 |
45
- | **AI Providers** | 4 (Anthropic, OpenAI, Ollama, Groq) |
46
45
  | **Slash Commands** | 50+ |
46
+ | **AI Providers** | 4 (Anthropic, OpenAI, Ollama, Groq) |
47
+ | **Bundled Resources** | 8,578 files (design guides, agent docs, karpathy tools, page agents) |
47
48
  | **Node.js** | >= 18.0.0 |
48
49
 
49
50
  ---
@@ -99,7 +100,7 @@ winter chat "explain this" --no-interactive
99
100
  Winter supports **4 AI providers** with automatic context routing:
100
101
 
101
102
  | Provider | Config Key | Default Model | Best For |
102
- |---|---|---|---|
103
+ |----------|------------|---------------|----------|
103
104
  | **Anthropic** | `anthropic` | `claude-sonnet-4-20250514` | Code review, refactoring, debugging |
104
105
  | **OpenAI** | `openai` | `gpt-4-turbo` | Documentation, explanations |
105
106
  | **Ollama** (local) | `ollama` | `llama3` | Privacy, offline use |
@@ -228,7 +229,7 @@ GROQ_API_KEY=gsk-...
228
229
  Winter provides **26 tools** -- fully compatible with Claude Code's tool interface + extras:
229
230
 
230
231
  | # | Tool | Description |
231
- |---|---|---|
232
+ |----|------|-------------|
232
233
  | 1 | **Read** | Read files with line numbers |
233
234
  | 2 | **Write** | Create new files |
234
235
  | 3 | **Edit** | Surgical string replacement (search & replace) |
@@ -272,12 +273,51 @@ Grep tool supports:
272
273
 
273
274
  ---
274
275
 
276
+ ## Bundled Resources
277
+
278
+ Winter ships with **7 integrated resource corpora** (8,578 files total):
279
+
280
+ | Resource | Files | Size | Description |
281
+ |----------|-------|------|-------------|
282
+ | **codex** | 206 | 116 MB | Codex CLI documentation & examples |
283
+ | **page-agent** | 239 | - | Alibaba GUI agent framework |
284
+ | **awesome-design-md** | 142 | 14 MB | 71 design systems (Airbnb, Apple, BMW, Claude, Coinbase, Cursor, Figma...) |
285
+ | **claude** | 795 | 3 MB | Claude Code prompts & patterns |
286
+ | **agents.md** | 62 | 2 MB | Agent documentation & examples |
287
+ | **karpathy-tools** | 2 | 1.3 MB | Karpathy's coding principles (Think Before Coding, Simplicity First, Surgical Changes, Goal-Driven Execution) |
288
+ | **ecc** | 7,932 | - | Embedded Context Corpus - searchable knowledge base |
289
+
290
+ ### Resource Commands
291
+
292
+ ```bash
293
+ # Browse karpathy-tools
294
+ /karpathy
295
+
296
+ # Browse codex resources
297
+ /codex
298
+
299
+ # Browse claude resources
300
+ /claude
301
+
302
+ # Browse agents.md
303
+ /agents
304
+
305
+ # Show all bundled resources
306
+ /resources
307
+
308
+ # Search ECC (Embedded Context Corpus)
309
+ winter ecc search "error handling"
310
+ winter ecc browse skills/error-handling
311
+ ```
312
+
313
+ ---
314
+
275
315
  ## Slash Commands
276
316
 
277
317
  ### Project & Session
278
318
 
279
319
  | Command | Description |
280
- |---|---|
320
+ |---------|-------------|
281
321
  | `/project` | Show/set current project |
282
322
  | `/pwd` | Show current directory |
283
323
  | `/cd <path>` | Change directory |
@@ -288,7 +328,7 @@ Grep tool supports:
288
328
  ### Memory & Plans
289
329
 
290
330
  | Command | Description |
291
- |---|---|
331
+ |---------|-------------|
292
332
  | `/remember <text>` | Store in memory |
293
333
  | `/memories` | Show stored memories |
294
334
  | `/forget [pattern]` | Clear memories |
@@ -301,7 +341,7 @@ Grep tool supports:
301
341
  ### Git & Automation
302
342
 
303
343
  | Command | Description |
304
- |---|---|
344
+ |---------|-------------|
305
345
  | `/commit` | Auto-generate commit message & commit |
306
346
  | `/review` | AI code review on current diff |
307
347
  | `/diff` | Preview git diff |
@@ -316,7 +356,7 @@ Grep tool supports:
316
356
  ### Tool Shortcuts
317
357
 
318
358
  | Command | Description |
319
- |---|---|
359
+ |---------|-------------|
320
360
  | `/read <file>` | Quick file read |
321
361
  | `/write <file>` | Quick file write (via editor) |
322
362
  | `/edit <file>` | Quick file edit |
@@ -329,7 +369,7 @@ Grep tool supports:
329
369
  ### Resources & Design
330
370
 
331
371
  | Command | Description |
332
- |---|---|
372
+ |---------|-------------|
333
373
  | `/codex` | Browse ~/.codex resources |
334
374
  | `/claude` | Browse ~/.claude resources |
335
375
  | `/karpathy` | Browse karpathy-tools |
@@ -340,6 +380,8 @@ Grep tool supports:
340
380
  | `/design add <brand>` | Add design system |
341
381
  | `/design list` | List design systems |
342
382
  | `/design preview <brand>` | Preview design system |
383
+ | `/ecc search <query>` | Search Embedded Context Corpus |
384
+ | `/ecc browse <path>` | Browse ECC section |
343
385
  | `/skill list` | List skills |
344
386
  | `/skill enable <name>` | Enable a skill |
345
387
  | `/skill create <name>` | Create a custom skill |
@@ -349,7 +391,7 @@ Grep tool supports:
349
391
  ### Provider & Config
350
392
 
351
393
  | Command | Description |
352
- |---|---|
394
+ |---------|-------------|
353
395
  | `/provider [name]` | Show/switch provider |
354
396
  | `/providers` | List all providers |
355
397
  | `/model [id]` | Show/set model |
@@ -364,7 +406,7 @@ Grep tool supports:
364
406
  ### Help & Exit
365
407
 
366
408
  | Command | Description |
367
- |---|---|
409
+ |---------|-------------|
368
410
  | `/help` or `/?` | Show help |
369
411
  | `/exit` or `/quit` | Exit Winter |
370
412
 
@@ -415,8 +457,8 @@ Tools with the `mcp__` prefix are automatically routed to the appropriate MCP se
415
457
 
416
458
  ```
417
459
  mcp__vscode__openFile -> VS Code extension server
418
- mcp__github__createPR -> GitHub integration server
419
- mcp__db__query -> Database server
460
+ mcp__github__createPR -> GitHub integration server
461
+ mcp__db__query -> Database server
420
462
  ```
421
463
 
422
464
  ---
@@ -443,7 +485,7 @@ winter session list # List all sessions
443
485
  ### Three-Tier Memory System
444
486
 
445
487
  | Level | Scope | Purpose |
446
- |---|---|---|
488
+ |-------|-------|---------|
447
489
  | **Working Memory** | Current session | Task context, conversation history |
448
490
  | **Project Memory** | Project-wide | Project-specific learnings & rules |
449
491
  | **Long-term Memory** | Cross-project | Reusable patterns & knowledge |
@@ -457,7 +499,7 @@ Winter supports hot-reloadable skills for specialized tasks.
457
499
  ### Built-in Skills
458
500
 
459
501
  | Skill | Description |
460
- |---|---|
502
+ |-------|-------------|
461
503
  | `coding` | Code analysis, generation, review |
462
504
  | `design` | Design system integration |
463
505
  | `debug` | Debugging assistance |
@@ -493,6 +535,31 @@ winter plugin remove <name> # Remove plugin
493
535
 
494
536
  ---
495
537
 
538
+ ## Embedded Context Corpus (ECC)
539
+
540
+ Winter includes a **searchable knowledge base** with 7,932 embedded documents covering:
541
+
542
+ - Error handling patterns
543
+ - Testing strategies
544
+ - Security best practices
545
+ - Performance optimization
546
+ - And more...
547
+
548
+ ### ECC Commands
549
+
550
+ ```bash
551
+ # Search the corpus
552
+ winter ecc search "error handling"
553
+ winter ecc search "async await best practices"
554
+
555
+ # Browse sections
556
+ winter ecc browse skills
557
+ winter ecc browse skills/error-handling
558
+ winter ecc browse testing/patterns
559
+ ```
560
+
561
+ ---
562
+
496
563
  ## Architecture
497
564
 
498
565
  ```
@@ -502,52 +569,69 @@ winter/
502
569
  |- src/
503
570
  | |- agent/
504
571
  | | - swe-agent.js # SWE-agent integration
572
+ | | - runtime.js # Agent runtime
573
+ | | - agent-definitions.js
505
574
  | |- ai/
506
575
  | | - providers.js # AI provider management + streaming
576
+ | | - orchestrator.js # AI orchestration
577
+ | | - reasoning.js # Reasoning patterns
578
+ | | - small-model-amplifier.js
579
+ | | - workflow-selector.js
507
580
  | |- cache/
508
- | | - system.js # Caching layer
581
+ | | - system.js # Caching layer
582
+ | | - embeddings.js # Embedding cache
509
583
  | |- cli/
510
- | | - commands.js # CLI command parser (50+ commands)
511
- | | - config.js # Config loader + secret management
512
- | | - conversation-format.js # Utility functions
513
- | | - markdown-format.js # Markdown rendering
514
- | | - prompt-builder.js # System prompt assembly
515
- | | - repl-commands.js # Slash command handlers (extracted)
516
- | | - repl.js # Interactive REPL loop
517
- | | - secret-env.js # Env file loader + redaction
518
- | | - slash-commands.js # Command definitions
519
- | | - snowflake-logo.js # Cyberpunk UI branding
520
- | | - spinner.js # Terminal spinner
521
- | | - terminal-ui.js # Terminal rendering utilities
584
+ | | - commands.js # CLI command parser (50+ commands)
585
+ | | - config.js # Config loader + secret management
586
+ | | - conversation-format.js
587
+ | | - markdown-format.js
588
+ | | - prompt-builder.js
589
+ | | - repl-commands.js
590
+ | | - repl.js # Interactive REPL loop
591
+ | | - secret-env.js # Env file loader + redaction
592
+ | | - slash-commands.js
593
+ | | - snowflake-logo.js # Cyberpunk UI branding
594
+ | | - spinner.js
595
+ | | - terminal-ui.js
596
+ | | - context-loader.js # Resource loading
597
+ | | - ecc.js # Embedded Context Corpus
598
+ | | - codebase-index/ # Fast semantic search
522
599
  | |- context/
523
- | | - compress.js # Conversation compression
524
- | | - router.js # Smart provider routing
600
+ | | - compress.js # Conversation compression
601
+ | | - router.js # Smart provider routing
602
+ | | - resource-loader.js
525
603
  | |- design/
526
- | | - commands.js # Design system commands
604
+ | | - commands.js # Design system commands
527
605
  | |- mcp/
528
- | | - client.js # MCP client
529
- | | - protocol.js # MCP protocol implementation
606
+ | | - client.js # MCP client
607
+ | | - protocol.js # MCP protocol implementation
530
608
  | |- plugins/
531
- | | - manager.js # Plugin manager
609
+ | | - manager.js # Plugin manager
532
610
  | |- session/
533
- | | - manager.js # Session manager
611
+ | | - manager.js # Session manager
534
612
  | |- skills/
535
- | | - manager.js # Skills manager
613
+ | | - manager.js # Skills manager
536
614
  | - tools/
537
- | - executor.js # Tool executor (25 tools)
538
- | - notebook.js # Jupyter notebook tools
539
- | - todo.js # Todo tools
540
- | - scheduler.js # Schedule wakeup tool
541
- | - interactive.js # Ask user tool
542
- | - agent.js # Sub-agent tool
543
- | - insert-text.js # Insert text tool
615
+ | - executor.js # Tool executor (25 tools)
616
+ | - notebook.js # Jupyter notebook tools
617
+ | - todo.js # Todo tools
618
+ | - scheduler.js # Schedule wakeup tool
619
+ | - interactive.js # Ask user tool
620
+ | - agent.js # Sub-agent tool
621
+ | - insert-text.js # Insert text tool
544
622
  | - str-replace-all.js # Batch replace tool
545
- | - web-archive.js # Web archive tool
546
- | - permission.js # Permission manager
547
- | - analytics.js # Tool usage tracking
548
- | - retry.js # Retry utility
623
+ | - web-archive.js # Web archive tool
624
+ | - permission.js # Permission manager
625
+ |- resources/local/
626
+ | |- codex/ # Codex CLI resources (206 files)
627
+ | |- page-agent/ # GUI automation (239 files)
628
+ | |- awesome-design-md/ # Design systems (142 files)
629
+ | |- claude/ # Claude patterns (795 files)
630
+ | |- agents.md/ # Agent docs (62 files)
631
+ | |- karpathy-tools/ # Coding principles (2 files)
632
+ | |- ecc/ # Embedded Context Corpus (7,932 files)
549
633
  |- README.md
550
- |- WINTER.md # Project rules for AI
634
+ |- WINTER.md # Project rules for AI
551
635
  |- package.json
552
636
  ```
553
637
 
@@ -555,17 +639,17 @@ winter/
555
639
 
556
640
  ## Core Philosophy
557
641
 
558
- Winter operates on three core principles:
642
+ Winter operates on **four core principles**:
559
643
 
560
644
  ### 1. Think Before Coding
561
- *Nghi truoc khi code*
645
+ *Nghi trước khi code*
562
646
 
563
647
  - State assumptions explicitly. If uncertain, ask.
564
648
  - Surface tradeoffs and alternatives -- don't pick silently.
565
649
  - If a simpler approach exists, say so. Push back when warranted.
566
650
 
567
651
  ### 2. Simplicity First
568
- *Don gian la tren het*
652
+ *Đơn giản trên hết*
569
653
 
570
654
  - Minimum code that solves the problem. Nothing speculative.
571
655
  - No features beyond what was asked.
@@ -573,13 +657,22 @@ Winter operates on three core principles:
573
657
  - No error handling for impossible scenarios.
574
658
 
575
659
  ### 3. Surgical Changes
576
- *Sua doi chinh xac*
660
+ *Sửa đổi chính xác*
577
661
 
578
662
  - Touch only what you must. Clean up only your own mess.
579
663
  - Don't "improve" adjacent code, comments, or formatting.
580
664
  - Don't refactor things that aren't broken.
581
665
  - Match existing style, even if you'd do it differently.
582
666
 
667
+ ### 4. Goal-Driven Execution
668
+ *Thực thi theo mục tiêu*
669
+
670
+ - Define success criteria clearly.
671
+ - Loop until verified -- don't claim completion without tool results.
672
+ - Verify each step with the closest command available.
673
+
674
+ > *These principles are integrated from [Karpathy Tools](resources/local/karpathy-tools/CLAUDE.md) and applied throughout Winter's operation.*
675
+
583
676
  ---
584
677
 
585
678
  ## Streaming & Real-time
@@ -600,7 +693,7 @@ User input -> REPL -> collectAssistantStream()
600
693
  ## Cross-Platform Support
601
694
 
602
695
  | Platform | Shell Support | Status |
603
- |---|---|---|
696
+ |----------|----------------|--------|
604
697
  | **Windows** | cmd, PowerShell, Git Bash | Full support |
605
698
  | **macOS** | zsh, bash | Full support |
606
699
  | **Linux** | bash, sh, zsh | Full support |
@@ -611,21 +704,6 @@ Winter auto-detects your platform and shell:
611
704
 
612
705
  ---
613
706
 
614
- ## Testing
615
-
616
- ```bash
617
- # Run all tests (213)
618
- npm test
619
-
620
- # Run specific test file
621
- node --test src/tools/executor.test.js
622
-
623
- # Run tests with watch mode
624
- node --test --watch src/**/*.test.js
625
- ```
626
-
627
- ---
628
-
629
707
  ## License
630
708
 
631
709
  MIT License -- see [LICENSE](LICENSE) for details.
@@ -638,4 +716,4 @@ MIT License -- see [LICENSE](LICENSE) for details.
638
716
 
639
717
  ---
640
718
 
641
- *Built with care -- Winter is the AI assistant that never sleeps.*
719
+ *Built with care -- Winter is the AI assistant that never sleeps.*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "winter-super-cli",
3
- "version": "2026.6.11",
3
+ "version": "2026.6.13",
4
4
  "description": "❄️ AI-Powered Development CLI with Interactive REPL",
5
5
  "type": "module",
6
6
  "main": "bin/winter.js",
@@ -9,7 +9,7 @@ export class AgentRuntime {
9
9
  this.repl = repl;
10
10
  }
11
11
 
12
- async runConversation(messages, label = 'Thinking', tools = null) {
12
+ async runConversation(messages, label = 'Thinking... (Đang suy nghĩ, tìm cách giải quyết)', tools = null) {
13
13
  const repl = this.repl;
14
14
  repl.spinner = new Spinner(label + '...');
15
15
  repl.spinner.start();
@@ -46,12 +46,17 @@ export class AgentRuntime {
46
46
  try {
47
47
  for (let i = 0; i < maxToolTurns; i++) {
48
48
  if (repl.isCancelled) throw new Error('AbortError');
49
+ if (repl.spinner) {
50
+ repl.spinner.update(`${label}...`);
51
+ repl.spinner.start();
52
+ }
49
53
  const turn = await repl.requestAssistantTurn(messages, {
50
54
  provider: executionProfile.provider,
51
55
  model: executionProfile.model,
52
56
  enableTools: true,
53
57
  toolPromptOnly: forceTextToolFallback,
54
58
  requireToolEvidence: requireToolEvidence && !usedTools,
59
+ usedMutatingTools: usedMutatingTools,
55
60
  }, startedAt, totalUsage);
56
61
 
57
62
  const assistantMsg = turn.assistantMsg || {};
@@ -64,12 +69,15 @@ export class AgentRuntime {
64
69
  if (toolCalls.length === 0) {
65
70
  if (turn.finishReason === 'tool_evidence_required') {
66
71
  noToolActionRetries++;
67
- if (noToolActionRetries > 2) {
68
- finalContent = 'Chưa thực hiện được: model trả lời mà không dùng tool nên Winter đã chặn để tránh báo xạo.';
72
+ if (noToolActionRetries > 3) {
73
+ finalContent = 'Chưa thực hiện được: model trả lời mà không dùng tool nên Winter đã chặn để tránh báo xạo. Hãy thử lại hoặc dùng model mạnh hơn.';
69
74
  console.log(`\n${colors.yellow}${finalContent}${colors.reset}\n`);
70
75
  reachedToolLimit = false;
71
76
  break;
72
77
  }
78
+ if (noToolActionRetries >= 2) {
79
+ console.log(`\n${colors.yellow}! Model không chịu dùng tool (lần ${noToolActionRetries}/3). Đang ép buộc lại...${colors.reset}`);
80
+ }
73
81
  messages.push({
74
82
  role: 'assistant',
75
83
  content: assistantMsg.content || '',
@@ -177,9 +185,14 @@ export class AgentRuntime {
177
185
  } else if (!proceed) {
178
186
  result = { success: false, error: 'User denied permission to execute this command.' };
179
187
  } else {
188
+ if (repl.spinner) {
189
+ repl.spinner.update(`Executing ${canonicalToolName}... (Đang chạy lệnh)`);
190
+ repl.spinner.start();
191
+ }
180
192
  result = toolName
181
193
  ? await repl.tools.execute(canonicalToolName, enrichedArgs, { cwd: repl.projectPath })
182
194
  : { success: false, error: 'Tool call is missing a tool name' };
195
+ if (repl.spinner) repl.spinner.stop();
183
196
  }
184
197
  const promptToolResult = await repl.buildPromptToolResultForModel(canonicalToolName, result);
185
198
  messages.push({
@@ -90,6 +90,7 @@ export class PromptBuilder {
90
90
  `Debug: reproduce or locate the failing path first, read the exact failing file/log, patch the smallest cause, then run the closest test/build/smoke command.`,
91
91
  `Design/UI: inspect existing UI and design resources first; deliver polished, responsive, non-generic interfaces, not placeholder layouts.`,
92
92
  `Images: if the user attaches or pastes an image, analyze it directly and connect findings to project files when relevant.`,
93
+ `CRITICAL: You MUST call tools (Read/Write/Edit/Bash) to do real work. NEVER write code in markdown and claim done. Winter blocks fake completions.`,
93
94
  `Tool fallback when native calls are unavailable: <invoke name="Read"><parameter name="path">README.md</parameter></invoke> OR {"tool":"Read","arguments":{"path":"README.md"}} OR CALL_TOOL Read {"path":"README.md"}.`,
94
95
  `Session: cwd=${this.projectPath}; id=${this.session?.getSessionId?.()?.substring(0, 8) || 'unknown'}`,
95
96
  `${requiredResourcesStr}${memoryStr}${plansStr}${skillsStr}${workflowStr}${blueprintStr}${startupPlanStr}${sessionSignalsStr}`,
@@ -114,6 +115,7 @@ export class PromptBuilder {
114
115
  `## Tool Usage`,
115
116
  `Use tools when they materially help. For coding tasks: inspect first, edit second, verify third.`,
116
117
  `Prefer Read/Grep/Glob before editing. Use Write/Edit for file changes.`,
118
+ `CRITICAL: When the user asks you to fix/create/edit/run/modify anything, you MUST call tools (Read, Write, Edit, Bash, etc.) to actually do it. NEVER just write code in a markdown code block and claim it is done. Winter will detect and block fake completions. If you say "đã sửa/đã tạo/done/fixed" without a tool call, your response will be rejected.`,
117
119
  `Tool call compatibility: if native tool calls are unavailable, output exactly one of these forms and no prose: <invoke name="Read"><parameter name="path">README.md</parameter></invoke> OR {"tool":"Read","arguments":{"path":"README.md"}} OR CALL_TOOL Read {"path":"README.md"}.`,
118
120
  `Browser capability: You CAN browse URLs! Use WebFetch to fetch page content (text extraction) or BrowserDebug for Chrome automation (JS rendering, screenshots). If user shares a URL or asks to view a website, use these tools automatically.`,
119
121
  `When a task touches coding, agents, UI, brand, or design, inspect the relevant required local resource in depth before deciding.`,
@@ -3,6 +3,8 @@ import { promises as fs } from 'fs';
3
3
  import { colors } from './snowflake-logo.js';
4
4
  import { SLASH_COMMANDS } from './slash-commands.js';
5
5
 
6
+ import { DesignCommands } from '../design/commands.js';
7
+
6
8
  function getPageAgentRoot(repl) {
7
9
  return repl.contextLoader?.getResourcePaths?.()?.pageAgent || path.join(repl.projectPath, 'resources', 'local', 'page-agent');
8
10
  }
@@ -735,11 +737,11 @@ export async function handleSlashCommand(repl, input) {
735
737
  break;
736
738
 
737
739
  // Design system
738
- case '/design':
739
- console.log(`${colors.yellow}Design commands:${colors.reset}`);
740
- console.log(` /design add <name> — Add a design system`);
741
- console.log(` /design search <query> — Search design systems`);
740
+ case '/design': {
741
+ const designCmd = new DesignCommands(repl);
742
+ await designCmd.execute(args[0], args.slice(1));
742
743
  break;
744
+ }
743
745
  case '/designs':
744
746
  await repl.showDesignSystems();
745
747
  break;
package/src/cli/repl.js CHANGED
@@ -1706,11 +1706,19 @@ ${colors.reset}
1706
1706
  const text = this.getLatestUserText(messages).toLowerCase();
1707
1707
  if (!text.trim()) return false;
1708
1708
 
1709
- const actionPattern = /\b(fix|repair|bug|debug|implement|create|write|edit|modify|update|delete|remove|refactor|run|test|build|commit|push|publish|install|check|inspect|read|scan|grep|search|change|apply|patch|sua|lam|tao|ghi|doc|xoa|chay|kiem tra|cai|them|doi|review|tim|sửa|làm|tạo|đọc|xóa|xoá|chạy|kiểm tra|cài|thêm|đổi|tìm)\b/i;
1710
- const targetPattern = /\b(file|repo|project|code|src|test|build|git|npm|node|folder|directory|cli|tool|provider|model|config|readme|package\.json|du an|thu muc|tap tin|loi|chuc nang|dự án|thư mục|tập tin|lỗi|chức năng)\b|[A-Za-z]:[\\/]|\.js\b|\.ts\b|\.tsx\b|\.json\b|\.md\b/i;
1711
- const pureQuestionPattern = /^(what|why|how|when|where|is|are|can|could|should|would|tai sao|vi sao|la gi|co nen|co phai|tại sao|vì sao|là gì|có nên|có phải)\b/i;
1709
+ // Direct action verbs (EN + VN)
1710
+ const actionPattern = /\b(fix|repair|bug|debug|implement|create|write|edit|modify|update|delete|remove|refactor|run|test|build|commit|push|publish|install|check|inspect|read|scan|grep|search|change|apply|patch|add|move|rename|copy|migrate|deploy|setup|configure|generate|format|clean|reset|revert|undo|merge|split|extract|inject|insert|replace|append|prepend|convert|transform|compile|execute|verify|validate|optimize|improve|enhance|upgrade|downgrade|enable|disable|toggle|set|connect|send|fetch|download|upload|open|close|start|stop|restart|sua|lam|tao|ghi|doc|xoa|chay|kiem tra|cai|them|doi|review|tim|viet|trien khai|cap nhat|xay dung|cau hinh|chinh|bo sung|loai bo|sửa|làm|tạo|đọc|xóa|xoá|chạy|kiểm tra|cài|thêm|đổi|tìm|viết|triển khai|cập nhật|xây dựng|cấu hình|chỉnh|bổ sung|loại bỏ|di chuyển|đổi tên|nâng cấp|tối ưu|kết nối|gửi|tải|mở|đóng|khởi động|dừng)\b/i;
1711
+ // Target patterns (filenames, paths, project terms)
1712
+ const targetPattern = /\b(file|repo|project|code|src|test|build|git|npm|node|folder|directory|cli|tool|provider|model|config|readme|package\.json|component|module|class|function|api|endpoint|route|page|style|css|html|template|schema|database|server|client|app|service|hook|context|store|reducer|middleware|controller|handler|view|layout|du an|thu muc|tap tin|loi|chuc nang|dự án|thư mục|tập tin|lỗi|chức năng|giao diện|trang|dịch vụ|thành phần|mô hình)\b|[A-Za-z]:[\\/]|\.jsx?\b|\.tsx?\b|\.json\b|\.md\b|\.py\b|\.css\b|\.html\b|\.vue\b|\.svelte\b|\.rs\b|\.go\b|\.java\b|\.c(pp)?\b|\.rb\b/i;
1713
+ // Pure questions that don't need tool action
1714
+ const pureQuestionPattern = /^(what|why|how|when|where|is|are|can|could|should|would|explain|describe|tell me|compare|giải thích|mô tả|so sánh|tai sao|vi sao|la gi|co nen|co phai|tại sao|vì sao|là gì|có nên|có phải|nhu the nao|như thế nào|khi nào)\b/i;
1712
1715
 
1713
1716
  if (pureQuestionPattern.test(text) && !actionPattern.test(text)) return false;
1717
+
1718
+ // Even without explicit target, some verbs are strong enough on their own
1719
+ 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;
1720
+ if (strongActionAlone.test(text)) return true;
1721
+
1714
1722
  return actionPattern.test(text) && targetPattern.test(text);
1715
1723
  }
1716
1724
 
@@ -1728,25 +1736,109 @@ ${colors.reset}
1728
1736
  const text = String(content || '').toLowerCase();
1729
1737
  if (!text.trim()) return false;
1730
1738
 
1731
- const clarification = /(?:cần thêm|cho mình|vui lòng|please provide|which file|what file|need more|clarify|không rõ|chưa rõ|file nào|thư mục nào|c?n th?m|cho m?nh|vui l?ng|kh?ng r?|ch?a r?|file n?o|th? m?c n?o)/i;
1739
+ // If model is asking for clarification, that's legitimate - no tool needed
1740
+ const clarification = /(?:cần thêm thông tin|cho mình biết|vui lòng cung cấp|please provide|which file|what file|need more info|clarify|không rõ|chưa rõ|file nào|thư mục nào|bạn muốn|you want me to|could you specify|can you tell)/i;
1732
1741
  if (clarification.test(text)) return false;
1742
+
1733
1743
  return true;
1734
1744
  }
1735
1745
 
1746
+ detectFakeCompletion(content = '') {
1747
+ const text = String(content || '').toLowerCase();
1748
+ if (!text.trim()) return false;
1749
+
1750
+ // Detect fake completion claims - model says it did something without using tools
1751
+ const fakeCompletionClaims = /(?:đã (?:sửa|tạo|viết|xóa|cập nhật|thêm|chỉnh|xong|hoàn thành|fix|update|edit|write|create|delete|remove|modify|change|apply|deploy|push)|i(?:'ve| have) (?:fixed|created|written|updated|added|modified|changed|edited|applied|deployed|deleted|removed|patched|implemented|refactored)|done!|xong rồi|hoàn thành|đã hoàn tất|hoàn tất|the (?:fix|change|update|edit|modification) (?:has been|is) (?:applied|done|completed|made)|here(?:'s| is) the (?:fix|update|change|solution|implementation|code)|file (?:has been|was) (?:updated|created|modified|written|changed)|changes? (?:have been|has been|were) (?:made|applied|saved)|successfully (?:updated|created|modified|fixed|applied|changed|written))/i;
1752
+ if (fakeCompletionClaims.test(text)) return true;
1753
+
1754
+ // Detect code blocks that pretend to show "changes" without tool use
1755
+ const codeBlockWithFilePath = /```[\s\S]*?(?:[\/\\][\w.-]+\.(?:js|ts|py|css|html|json|md|jsx|tsx|vue|go|rs|java|c|cpp|rb|sh))[\s\S]*?```/i;
1756
+ const claimsFileChange = /(?:here(?:'s| is)|below|sau đây|dưới đây|như sau|updated|modified|changed|new|fixed)/i;
1757
+ if (codeBlockWithFilePath.test(text) && claimsFileChange.test(text)) return true;
1758
+
1759
+ return false;
1760
+ }
1761
+
1736
1762
  buildToolEvidenceCorrection(messages = []) {
1737
1763
  const request = this.getLatestUserText(messages);
1738
1764
  return [
1739
- 'Runtime correction: the user requested an action that requires tool evidence.',
1740
- 'Your previous response did not use any tool, so it was blocked to avoid falsely claiming completion.',
1741
- 'Now use the available tools to inspect/edit/run/check as needed. Do not say the task is done until a tool result proves it.',
1742
- 'If native tool calls are not supported by this model/provider, output exactly one fallback tool call and no prose, for example:',
1765
+ '⚠️ RUNTIME ENFORCEMENT: Your previous response was BLOCKED because you did not use any tool.',
1766
+ '',
1767
+ 'The user requested an action that requires REAL tool execution. You MUST:',
1768
+ '1. Call Read/Grep/Glob to inspect the relevant files FIRST',
1769
+ '2. Call Write/Edit to make changes',
1770
+ '3. Call Bash to run/test/verify',
1771
+ '',
1772
+ 'DO NOT write code in a code block and claim it is done. That is a hallucination.',
1773
+ 'DO NOT say "I have updated/created/fixed" without a tool call proving it.',
1774
+ 'DO NOT describe what you would do. Actually DO IT with tool calls.',
1775
+ '',
1776
+ 'Available tools: Read, Write, Edit, Bash, Glob, Grep, BrowserDebug, WebFetch, WebSearch.',
1777
+ '',
1778
+ 'If native tool calls are not supported, output exactly one fallback tool call:',
1743
1779
  '<invoke name="Read"><parameter name="path">README.md</parameter></invoke>',
1744
1780
  '{"tool":"Read","arguments":{"path":"README.md"}}',
1745
1781
  'CALL_TOOL Read {"path":"README.md"}',
1782
+ '',
1746
1783
  `Original user request: ${request}`,
1747
1784
  ].join('\n');
1748
1785
  }
1749
1786
 
1787
+ /**
1788
+ * Smart Tool Routing: phân tích câu lệnh user và gợi ý tool phù hợp.
1789
+ * Giúp model yếu/nhỏ chọn đúng tool thay vì dùng Bash cho mọi thứ.
1790
+ */
1791
+ buildToolRoutingHint(userMessage = '') {
1792
+ const text = String(userMessage || '').toLowerCase();
1793
+ if (!text.trim()) return null;
1794
+
1795
+ const hints = [];
1796
+
1797
+ // Detect file reading requests
1798
+ const hasPath = /[A-Za-z]:[\\/][\w.\\/\\-]+/i.test(text) || /(?:^|\s)[.~]?\/[\w.\/-]+/i.test(text);
1799
+ const readVerbs = /\b(đọc|doc|read|xem|view|mở|open|show|hiện|hiển thị|cat|type)\b/i;
1800
+ const readFilePatterns = /\b(đọc|doc|read|xem|view|mở|open|show|hiện|cat|type)\b.*\.(?:js|ts|py|json|md|css|html|txt|yaml|yml|toml|cfg|ini|env|sh|bat|ps1|xml|vue|svelte|go|rs|java|c|cpp|rb|php)\b/i;
1801
+ const dirPatterns = /\b(đọc|doc|read|xem|liệt kê|list|ls|dir|show|hiện)\b.*\b(thư mục|folder|directory|dir)\b/i;
1802
+
1803
+ if (readFilePatterns.test(text)) {
1804
+ hints.push('TOOL HINT: To read a file, call tool Read with {"file_path": "<path>"}. Do NOT use Bash with cat/type.');
1805
+ } else if (hasPath && readVerbs.test(text)) {
1806
+ // Path detected + read verb, could be file or directory
1807
+ hints.push('TOOL HINT: To read a file, call tool Read with {"file_path": "<path>"}. To list a directory, call Glob with {"pattern": "*", "cwd": "<path>"}. Do NOT use Bash with ls/dir/cat/type.');
1808
+ } else if (dirPatterns.test(text)) {
1809
+ hints.push('TOOL HINT: To list directory contents, call tool Glob with {"pattern": "*", "cwd": "<path>"}. Do NOT use Bash with ls/dir.');
1810
+ }
1811
+
1812
+ // Detect file writing/creating requests
1813
+ if (/\b(tạo|tao|create|viết|viet|write|ghi)\b.*\b(file|tập tin|tap tin)\b/i.test(text)) {
1814
+ hints.push('TOOL HINT: To create/write a file, call tool Write with {"file_path": "<path>", "content": "<content>"}. Do NOT use Bash with echo/cat.');
1815
+ }
1816
+
1817
+ // Detect file editing requests
1818
+ if (/\b(sửa|sua|edit|chỉnh|chinh|thay|replace|đổi|doi|modify|update|cập nhật)\b.*(?:file|\.(?:js|ts|py|json|md|css|html)\b)/i.test(text)) {
1819
+ hints.push('TOOL HINT: To edit a file, first Read it, then call Edit with {"file_path": "<path>", "old_string": "<exact text>", "new_string": "<replacement>"}.');
1820
+ }
1821
+
1822
+ // Detect search/find requests
1823
+ if (/\b(tìm|tim|find|search|kiếm|kiem|grep|ở đâu|o dau|where)\b/i.test(text) && !(/\b(web|google|online|internet)\b/i.test(text))) {
1824
+ hints.push('TOOL HINT: To search in code, call tool Grep with {"pattern": "<search term>", "path": "<directory>"}. Do NOT use Bash with grep/findstr.');
1825
+ }
1826
+
1827
+ // Detect test/run requests
1828
+ if (/\b(chạy|chay|run|test|execute|thực thi|thuc thi|build|compile|npm|node|python|pip)\b/i.test(text) && !readPatterns.test(text)) {
1829
+ hints.push('TOOL HINT: To run commands, call tool Bash with {"command": "<command>"}.');
1830
+ }
1831
+
1832
+ // Detect URL/web requests
1833
+ if (/\b(https?:\/\/[^\s]+|url|website|trang web|web page)\b/i.test(text)) {
1834
+ hints.push('TOOL HINT: To fetch a URL, call tool WebFetch with {"url": "<url>"}. For browser debugging, use BrowserDebug.');
1835
+ }
1836
+
1837
+ if (hints.length === 0) return null;
1838
+
1839
+ return `[Tool Router] ${hints.join(' | ')}`;
1840
+ }
1841
+
1750
1842
  withCurrentAbortSignal(options = {}) {
1751
1843
  const signal = options.signal || options.abortSignal || this.currentAbortController?.signal;
1752
1844
  return signal ? { ...options, signal } : options;
@@ -1810,8 +1902,11 @@ ${colors.reset}
1810
1902
  }
1811
1903
  const finishReason = response.choices?.[0]?.finish_reason;
1812
1904
 
1905
+ if (this.spinner) this.spinner.stop();
1906
+
1813
1907
  if (assistantMsg.content && toolCalls.length === 0) {
1814
- if (options?.requireToolEvidence && this.responseNeedsToolEvidence(assistantMsg.content)) {
1908
+ const isFakeCompletion = !options?.usedMutatingTools && this.detectFakeCompletion(assistantMsg.content);
1909
+ if ((options?.requireToolEvidence && this.responseNeedsToolEvidence(assistantMsg.content)) || isFakeCompletion) {
1815
1910
  return { assistantMsg, toolCalls, finalContent: '', finishReason: 'tool_evidence_required' };
1816
1911
  }
1817
1912
  this.printAssistantAnswer(assistantMsg.content, startedAt, totalUsage);
@@ -1823,6 +1918,8 @@ ${colors.reset}
1823
1918
 
1824
1919
  async collectAssistantStream(messages, options, startedAt, totalUsage) {
1825
1920
  let content = '';
1921
+ let streamBuffer = '';
1922
+ let printedLines = 0;
1826
1923
  const toolCallParts = [];
1827
1924
  let finishReason = null;
1828
1925
  let printed = false;
@@ -1878,6 +1975,18 @@ ${colors.reset}
1878
1975
 
1879
1976
  if (chunk.content) {
1880
1977
  content += chunk.content;
1978
+ if (bufferToolModeContent && this.spinner && process.env.NODE_ENV !== 'test') {
1979
+ streamBuffer += chunk.content;
1980
+ let newlineIdx;
1981
+ const terminalCols = process.stdout.columns || 80;
1982
+ while ((newlineIdx = streamBuffer.indexOf('\n')) !== -1) {
1983
+ const line = streamBuffer.slice(0, newlineIdx).replace(/\r$/, '');
1984
+ streamBuffer = streamBuffer.slice(newlineIdx + 1);
1985
+ const visibleLength = line.replace(/\x1b\[[0-9;]*m/g, '').length + 2;
1986
+ printedLines += Math.max(1, Math.ceil(visibleLength / terminalCols));
1987
+ process.stdout.write(`\r\x1b[K${colors.dim}│ ${colors.reset}${colors.dim}${line}${colors.reset}\n`);
1988
+ }
1989
+ }
1881
1990
  }
1882
1991
 
1883
1992
  if (this.spinner && printed === false) {
@@ -1887,16 +1996,20 @@ ${colors.reset}
1887
1996
  const args = lastCall.function.arguments || '';
1888
1997
  let summary = args.replace(/\s+/g, ' ');
1889
1998
  if (summary.length > 60) summary = '...' + summary.slice(-60);
1890
- this.spinner.update(`Calling ${lastCall.function.name}... ${summary}`);
1999
+ this.spinner.update(`Calling ${lastCall.function.name}... (Chuẩn bị gọi tool) ${summary}`);
1891
2000
  }
1892
- } else if (content.length > 0 && bufferToolModeContent) {
1893
- let summary = content.replace(/\s+/g, ' ');
1894
- if (summary.length > 60) summary = '...' + summary.slice(-60);
1895
- this.spinner.update(`Generating... ${summary}`);
1896
2001
  }
1897
2002
  }
1898
2003
  }
1899
2004
 
2005
+ if (streamBuffer.length > 0 && this.spinner) {
2006
+ const line = streamBuffer.replace(/\r$/, '');
2007
+ const terminalCols = process.stdout.columns || 80;
2008
+ const visibleLength = line.replace(/\x1b\[[0-9;]*m/g, '').length + 2;
2009
+ printedLines += Math.max(1, Math.ceil(visibleLength / terminalCols));
2010
+ process.stdout.write(`\r\x1b[K${colors.dim}│ ${colors.reset}${colors.dim}${line}${colors.reset}\n`);
2011
+ }
2012
+
1900
2013
  if (this.spinner) this.spinner.stop();
1901
2014
 
1902
2015
  const inlineToolExtraction = this.extractInlineToolCalls(content);
@@ -1911,7 +2024,15 @@ ${colors.reset}
1911
2024
  const visibleContent = inlineToolExtraction.content || content;
1912
2025
 
1913
2026
  if (toolCalls.length === 0 && visibleContent) {
1914
- if (options?.requireToolEvidence && this.responseNeedsToolEvidence(visibleContent)) {
2027
+ if (printedLines > 0 && bufferToolModeContent) {
2028
+ const linesToErase = Math.min(printedLines, (process.stdout.rows || 24) - 2);
2029
+ if (linesToErase > 0) {
2030
+ process.stdout.write(`\r\x1b[K\x1b[${linesToErase}A\x1b[J`);
2031
+ }
2032
+ }
2033
+
2034
+ const isFakeCompletion = !options?.usedMutatingTools && this.detectFakeCompletion(visibleContent);
2035
+ if ((options?.requireToolEvidence && this.responseNeedsToolEvidence(visibleContent)) || isFakeCompletion) {
1915
2036
  return {
1916
2037
  assistantMsg: { content: visibleContent },
1917
2038
  toolCalls,
@@ -2067,6 +2188,11 @@ ${colors.reset}
2067
2188
  ];
2068
2189
 
2069
2190
  try {
2191
+ if (this.spinner) {
2192
+ this.spinner.update('Thinking (final answer)... (Đang viết câu trả lời cuối)');
2193
+ this.spinner.start();
2194
+ }
2195
+
2070
2196
  if (typeof this.ai.streamRequest === 'function') {
2071
2197
  return await this.streamFinalAnswer(finalMessages, startedAt, totalUsage, executionProfile);
2072
2198
  }
@@ -2079,11 +2205,15 @@ ${colors.reset}
2079
2205
  });
2080
2206
  this.addUsage(totalUsage, response.usage);
2081
2207
  const content = response.choices?.[0]?.message?.content || '';
2208
+
2209
+ if (this.spinner) this.spinner.stop();
2210
+
2082
2211
  if (content) {
2083
2212
  this.printAssistantAnswer(content, startedAt, totalUsage);
2084
2213
  }
2085
2214
  return content;
2086
2215
  } catch (error) {
2216
+ if (this.spinner) this.spinner.stop();
2087
2217
  if (this.isAbortError(error)) throw new Error('AbortError');
2088
2218
  const fallback = this.buildToolFallbackAnswer(toolSummaries, error.message);
2089
2219
  console.log(`\n${colors.yellow}${fallback}${colors.reset}\n`);
@@ -2110,6 +2240,9 @@ ${colors.reset}
2110
2240
  if (chunk.content) {
2111
2241
  content += chunk.content;
2112
2242
  }
2243
+ if (this.spinner && isFirst === false) {
2244
+ this.spinner.update('Generating...');
2245
+ }
2113
2246
  }
2114
2247
 
2115
2248
  if (this.spinner) this.spinner.stop();
@@ -2450,6 +2583,13 @@ ${colors.reset}
2450
2583
  }
2451
2584
 
2452
2585
  const tools = this.getAgentTools('general');
2586
+
2587
+ // Smart tool routing: inject a hint for weak models
2588
+ const toolHint = this.buildToolRoutingHint(message);
2589
+ if (toolHint) {
2590
+ messages.push({ role: 'system', content: toolHint });
2591
+ }
2592
+
2453
2593
  const { finalContent, usedMutatingTools } = await this.runConversation(messages, 'Thinking', tools);
2454
2594
 
2455
2595
  const allToolCalls = [];
@@ -35,7 +35,7 @@ export const SLASH_COMMANDS = [
35
35
  { cmd: '/grep', desc: 'Search files', usage: '/grep <pattern>' },
36
36
  { cmd: '/bash', desc: 'Run command', usage: '/bash <command>' },
37
37
  { cmd: '/image', desc: 'Analyze image/screenshot or clipboard image', usage: '/image [file] [question]' },
38
- { cmd: '/design', desc: 'Design commands', sub: ['search', 'add', 'list', 'preview'] },
38
+ { cmd: '/design', desc: 'Design commands', sub: ['search', 'add', 'apply', 'list', 'preview'] },
39
39
  { cmd: '/designs', desc: 'List/search awesome-design-md systems', usage: '/designs [query]' },
40
40
  { cmd: '/skill', desc: 'Skills management', sub: ['list', 'enable', 'create'] },
41
41
  { cmd: '/skills', desc: 'List local Winter/Codex/Claude skills' },
@@ -14,6 +14,11 @@ export class Spinner {
14
14
  const reset = this.colors.reset || '';
15
15
  const dim = this.colors.dim || '';
16
16
  this.startTime = Date.now();
17
+ this.lastLines = 0;
18
+
19
+ // Make sure we're on a clean line
20
+ process.stdout.write('\r\x1b[K');
21
+
17
22
  this.interval = setInterval(() => {
18
23
  const elapsed = Math.floor((Date.now() - this.startTime) / 1000);
19
24
  let timeStr = '';
@@ -24,25 +29,17 @@ export class Spinner {
24
29
  const cols = process.stdout.columns || 80;
25
30
  let fullStr = `${this.frames[this.frameIndex]} ${this.text}${timeStr}`;
26
31
 
27
- // Clean ANSI for length calculation (approximate)
28
- const visibleLen = fullStr.replace(/\x1b\[[0-9;]*m/g, '').length;
29
-
30
- if (visibleLen >= cols - 1) {
31
- // Truncate the text part, leave room for timeStr and frame
32
- const timeStrLen = timeStr.length;
33
- const availableSpaceForText = cols - timeStrLen - 6;
34
- if (availableSpaceForText > 10) {
35
- const truncText = this.text.length > availableSpaceForText
36
- ? '...' + this.text.slice(-(availableSpaceForText - 3))
37
- : this.text;
38
- fullStr = `${this.frames[this.frameIndex]} ${truncText}${timeStr}`;
39
- } else {
40
- // Terminal is extremely narrow, just truncate everything
41
- fullStr = fullStr.slice(0, cols - 4) + '...';
42
- }
32
+ // Clear previous frame
33
+ if (this.lastLines > 0) {
34
+ process.stdout.write(`\r\x1b[${this.lastLines}A\x1b[J`);
35
+ } else {
36
+ process.stdout.write('\r\x1b[K');
43
37
  }
38
+
39
+ const visibleLen = fullStr.replace(/\x1b\[[0-9;]*m/g, '').length;
40
+ this.lastLines = Math.max(0, Math.ceil(visibleLen / cols) - 1);
44
41
 
45
- process.stdout.write(`\r\x1b[K${cyan}${fullStr.slice(0, 2)}${reset}${dim}${fullStr.slice(2)}${reset}`);
42
+ process.stdout.write(`${cyan}${fullStr.slice(0, 2)}${reset}${dim}${fullStr.slice(2)}${reset}`);
46
43
  this.frameIndex = (this.frameIndex + 1) % this.frames.length;
47
44
  }, 80);
48
45
  }
@@ -51,7 +48,16 @@ export class Spinner {
51
48
  if (!this.interval) return;
52
49
  clearInterval(this.interval);
53
50
  this.interval = null;
54
- process.stdout.write(`\r\x1b[K${finalText ? `${finalText}\n` : ''}`);
51
+
52
+ if (this.lastLines > 0) {
53
+ process.stdout.write(`\r\x1b[${this.lastLines}A\x1b[J`);
54
+ } else {
55
+ process.stdout.write('\r\x1b[K');
56
+ }
57
+
58
+ if (finalText) {
59
+ process.stdout.write(`${finalText}\n`);
60
+ }
55
61
  }
56
62
 
57
63
  update(text) {
@@ -11,9 +11,10 @@ import { colors, statusIcons } from '../cli/snowflake-logo.js';
11
11
  const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
12
12
 
13
13
  export class DesignCommands {
14
- constructor(session, config) {
15
- this.session = session;
16
- this.config = config;
14
+ constructor(repl) {
15
+ this.repl = repl;
16
+ this.session = repl.session;
17
+ this.config = repl.config;
17
18
  this.brandsDir = path.join(packageRoot, 'resources', 'local', 'awesome-design-md', 'design-md');
18
19
  }
19
20
 
@@ -26,6 +27,9 @@ export class DesignCommands {
26
27
  case 'add':
27
28
  await this.addBrand(args[0]);
28
29
  break;
30
+ case 'apply':
31
+ await this.applyBrand(args[0]);
32
+ break;
29
33
  case 'list':
30
34
  await this.listBrands();
31
35
  break;
@@ -109,6 +113,51 @@ export class DesignCommands {
109
113
  }
110
114
  }
111
115
 
116
+ async applyBrand(brand) {
117
+ if (!brand) {
118
+ console.log(`${colors.yellow}Usage: winter design apply <brand>${colors.reset}`);
119
+ return;
120
+ }
121
+
122
+ try {
123
+ const brandDir = path.join(this.brandsDir, brand);
124
+ let fileContent = null;
125
+ let fileName = null;
126
+
127
+ const designPath = path.join(brandDir, 'DESIGN.md');
128
+ const readmePath = path.join(brandDir, 'README.md');
129
+
130
+ if (await fs.access(designPath).then(() => true).catch(() => false)) {
131
+ fileContent = await fs.readFile(designPath, 'utf8');
132
+ fileName = 'DESIGN.md';
133
+ } else if (await fs.access(readmePath).then(() => true).catch(() => false)) {
134
+ fileContent = await fs.readFile(readmePath, 'utf8');
135
+ fileName = 'README.md';
136
+ }
137
+
138
+ if (!fileContent) {
139
+ console.log(`${colors.red}${statusIcons.error} Brand "${brand}" not found${colors.reset}`);
140
+ return;
141
+ }
142
+
143
+ console.log(`${colors.cyan}${statusIcons.info} Analyzing and applying ${brand} design system...${colors.reset}`);
144
+
145
+ const prompt = `Please act as a Senior UI/UX Engineer. Analyze the following design system (${brand}) and completely refactor the UI and styles in this project to match its specifications. Focus on colors, typography, border radiuses, interactive states, and overall visual aesthetics as defined in the document.
146
+
147
+ <design_system>
148
+ ${fileContent}
149
+ </design_system>
150
+
151
+ Start by reviewing the codebase, especially tailwind configs or global css, then rewrite the main components. Create a plan if needed.`;
152
+
153
+ // Inject the task to the AI REPL loop
154
+ await this.repl.chat(prompt);
155
+
156
+ } catch (error) {
157
+ console.log(`${colors.red}${statusIcons.error} Error: ${error.message}${colors.reset}`);
158
+ }
159
+ }
160
+
112
161
  async listBrands() {
113
162
  try {
114
163
  const brands = await fs.readdir(this.brandsDir);