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 +154 -76
- package/package.json +1 -1
- package/src/agent/runtime.js +16 -3
- package/src/cli/prompt-builder.js +2 -0
- package/src/cli/repl-commands.js +6 -4
- package/src/cli/repl.js +155 -15
- package/src/cli/slash-commands.js +1 -1
- package/src/cli/spinner.js +24 -18
- package/src/design/commands.js +52 -3
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
|
|
5
|
+
> *Winter là một trợ lý 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 và Codex CLI, Winter mang đến giao diện Cyberpunk đẹp mắt, quản lý phiên làm việc thông minh, khả năng agent tự trị, và 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
|
|
41
|
-
| **Source
|
|
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
|
|
419
|
-
mcp__db__query
|
|
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
|
|
581
|
+
| | - system.js # Caching layer
|
|
582
|
+
| | - embeddings.js # Embedding cache
|
|
509
583
|
| |- cli/
|
|
510
|
-
| | - commands.js
|
|
511
|
-
| | - config.js
|
|
512
|
-
| | - conversation-format.js
|
|
513
|
-
| | - markdown-format.js
|
|
514
|
-
| | - prompt-builder.js
|
|
515
|
-
| | - repl-commands.js
|
|
516
|
-
| | - repl.js
|
|
517
|
-
| | - secret-env.js
|
|
518
|
-
| | - slash-commands.js
|
|
519
|
-
| | - snowflake-logo.js
|
|
520
|
-
| | - spinner.js
|
|
521
|
-
| | - terminal-ui.js
|
|
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
|
|
524
|
-
| | - router.js
|
|
600
|
+
| | - compress.js # Conversation compression
|
|
601
|
+
| | - router.js # Smart provider routing
|
|
602
|
+
| | - resource-loader.js
|
|
525
603
|
| |- design/
|
|
526
|
-
| | - commands.js
|
|
604
|
+
| | - commands.js # Design system commands
|
|
527
605
|
| |- mcp/
|
|
528
|
-
| | - client.js
|
|
529
|
-
| | - protocol.js
|
|
606
|
+
| | - client.js # MCP client
|
|
607
|
+
| | - protocol.js # MCP protocol implementation
|
|
530
608
|
| |- plugins/
|
|
531
|
-
| | - manager.js
|
|
609
|
+
| | - manager.js # Plugin manager
|
|
532
610
|
| |- session/
|
|
533
|
-
| | - manager.js
|
|
611
|
+
| | - manager.js # Session manager
|
|
534
612
|
| |- skills/
|
|
535
|
-
| | - manager.js
|
|
613
|
+
| | - manager.js # Skills manager
|
|
536
614
|
| - tools/
|
|
537
|
-
| - executor.js
|
|
538
|
-
| - notebook.js
|
|
539
|
-
| - todo.js
|
|
540
|
-
| - scheduler.js
|
|
541
|
-
| - interactive.js
|
|
542
|
-
| - agent.js
|
|
543
|
-
| - insert-text.js
|
|
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
|
|
546
|
-
| - permission.js
|
|
547
|
-
|
|
548
|
-
|
|
|
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
|
|
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
|
|
642
|
+
Winter operates on **four core principles**:
|
|
559
643
|
|
|
560
644
|
### 1. Think Before Coding
|
|
561
|
-
*Nghi
|
|
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
|
-
|
|
652
|
+
*Đơn giản là 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
|
-
*
|
|
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
package/src/agent/runtime.js
CHANGED
|
@@ -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 >
|
|
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.`,
|
package/src/cli/repl-commands.js
CHANGED
|
@@ -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
|
-
|
|
740
|
-
|
|
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
|
-
|
|
1710
|
-
const
|
|
1711
|
-
|
|
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
|
-
|
|
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
|
-
'
|
|
1740
|
-
'
|
|
1741
|
-
'
|
|
1742
|
-
'
|
|
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
|
-
|
|
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 (
|
|
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' },
|
package/src/cli/spinner.js
CHANGED
|
@@ -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
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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(
|
|
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
|
-
|
|
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) {
|
package/src/design/commands.js
CHANGED
|
@@ -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(
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
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);
|