winter-super-cli 2026.5.24 → 2026.5.25

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.
Files changed (62) hide show
  1. package/README.md +1 -1
  2. package/WINTER.md +6 -0
  3. package/bin/winter.js +77 -220
  4. package/package.json +1 -1
  5. package/resources/local/manifest.json +60 -57
  6. package/src/ai/providers.js +38 -11
  7. package/src/cli/commands.js +24 -3
  8. package/src/cli/commands.test.js +116 -0
  9. package/src/cli/config.js +12 -0
  10. package/src/cli/repl.js +465 -146
  11. package/src/cli/repl.test.js +203 -2
  12. package/src/cli/snowflake-logo.js +15 -7
  13. package/src/cli/terminal-ui.js +125 -0
  14. package/src/cli/terminal-ui.test.js +33 -0
  15. package/src/plugins/manager.js +3 -1
  16. package/src/session/manager.js +44 -0
  17. package/src/session/manager.test.js +72 -0
  18. package/src/tools/executor.js +1 -1
  19. package/src/tools/executor.test.js +110 -0
  20. package/resources/local/claude/settings.json +0 -33
  21. package/resources/local/claude/todos/022bdc3c-e2c0-4a20-a74f-b348ed022c75-agent-022bdc3c-e2c0-4a20-a74f-b348ed022c75.json +0 -1
  22. package/resources/local/claude/todos/316f0e7d-5512-49fa-8c7f-edc75b777612-agent-316f0e7d-5512-49fa-8c7f-edc75b777612.json +0 -1
  23. package/resources/local/claude/todos/3676dc17-fca1-4692-934b-ce35e1965af6-agent-3676dc17-fca1-4692-934b-ce35e1965af6.json +0 -1
  24. package/resources/local/claude/todos/464493de-7f2a-45cf-93e8-ad73214afa10-agent-464493de-7f2a-45cf-93e8-ad73214afa10.json +0 -1
  25. package/resources/local/claude/todos/51f2e7a7-3f31-4692-a9b2-d3f3906aafea-agent-51f2e7a7-3f31-4692-a9b2-d3f3906aafea.json +0 -1
  26. package/resources/local/claude/todos/64a67dce-3d62-4a98-a548-b9c91a8e87e8-agent-64a67dce-3d62-4a98-a548-b9c91a8e87e8.json +0 -1
  27. package/resources/local/claude/todos/727a06e6-0ac2-41ca-8b81-2c14e4d40182-agent-727a06e6-0ac2-41ca-8b81-2c14e4d40182.json +0 -1
  28. package/resources/local/claude/todos/7d34d296-9b5a-4525-9b68-600d2ae20b59-agent-7d34d296-9b5a-4525-9b68-600d2ae20b59.json +0 -1
  29. package/resources/local/claude/todos/8c0606f1-5bcc-4176-8125-c5174fd69002-agent-8c0606f1-5bcc-4176-8125-c5174fd69002.json +0 -1
  30. package/resources/local/claude/todos/905aab16-5225-43f6-8ae4-c94491fd3a6f-agent-905aab16-5225-43f6-8ae4-c94491fd3a6f.json +0 -1
  31. package/resources/local/claude/todos/9dbe93f0-d62c-4c12-b4eb-0eecc437d625-agent-9dbe93f0-d62c-4c12-b4eb-0eecc437d625.json +0 -1
  32. package/resources/local/claude/todos/ad48500f-02a5-4f18-970b-82fb595d171f-agent-ad48500f-02a5-4f18-970b-82fb595d171f.json +0 -1
  33. package/resources/local/claude/todos/af86ea71-9907-4066-907c-68055e6c0081-agent-af86ea71-9907-4066-907c-68055e6c0081.json +0 -1
  34. package/resources/local/claude/todos/dbb0dc16-5d71-4f1d-a56c-db0741b3d485-agent-dbb0dc16-5d71-4f1d-a56c-db0741b3d485.json +0 -1
  35. package/resources/local/claude/todos/ff1ac487-eb0f-4c63-9360-fbb0a81bb5ae-agent-ff1ac487-eb0f-4c63-9360-fbb0a81bb5ae.json +0 -1
  36. package/resources/local/codex/config.toml +0 -84
  37. package/resources/local/codex/memories/MEMORY.md +0 -972
  38. package/resources/local/codex/memories/extensions/ad_hoc/instructions.md +0 -13
  39. package/resources/local/codex/memories/memory_summary.md +0 -188
  40. package/resources/local/codex/memories/raw_memories.md +0 -1488
  41. package/resources/local/codex/memories/rollout_summaries/2026-03-27T04-05-14-Iirb-nsis_full_installer_build_cpp_ocr_translator.md +0 -46
  42. package/resources/local/codex/memories/rollout_summaries/2026-03-28T06-18-17-Si3U-my_translator_overlay_lockfix_portable_nsis.md +0 -112
  43. package/resources/local/codex/memories/rollout_summaries/2026-04-15T06-42-11-2JMi-qelasy_timeout_and_watch_control_stability.md +0 -90
  44. package/resources/local/codex/memories/rollout_summaries/2026-04-16T03-12-59-z6Wi-request_all_row_click_detail_navigation.md +0 -42
  45. package/resources/local/codex/memories/rollout_summaries/2026-04-17T05-49-03-tNBk-my_translator_project_readability_audio_latency_clear_button.md +0 -75
  46. package/resources/local/codex/memories/rollout_summaries/2026-04-21T04-05-04-EXnh-nsis_packaging_harfbuzz_dll_qml_runtime_debug.md +0 -108
  47. package/resources/local/codex/memories/rollout_summaries/2026-04-22T03-48-40-VnNG-openclaw_opencode_sync_and_runtime_repair.md +0 -86
  48. package/resources/local/codex/memories/rollout_summaries/2026-04-22T06-49-49-R8yZ-web_book_user_portal_and_lint_fixes.md +0 -82
  49. package/resources/local/codex/memories/rollout_summaries/2026-04-22T06-50-35-ZaS1-smoke_admin_rbac_refund_connection_refused.md +0 -35
  50. package/resources/local/codex/memories/rollout_summaries/2026-04-22T11-05-04-aotT-nextjs_build_fix_statswidget_leaflet_ssr.md +0 -78
  51. package/resources/local/codex/memories/rollout_summaries/2026-04-23T03-22-24-a5q4-ui_still_looks_cloudflare_only.md +0 -41
  52. package/resources/local/codex/memories/rollout_summaries/2026-04-23T04-35-47-amlb-bayre247_hero_slide_above_search_form.md +0 -49
  53. package/resources/local/codex/memories/rollout_summaries/2026-04-23T04-59-21-lZWv-ocr_backend_parity_easyocr_tesseract_paddle_fallback.md +0 -92
  54. package/resources/local/codex/memories/rollout_summaries/2026-04-23T07-36-22-tPuo-request_workflow_editor_drag_edge_smaller_arrows_roadmap.md +0 -72
  55. package/resources/local/codex/memories/rollout_summaries/2026-04-24T08-01-05-Gb3B-checkin_shifts_workdays_assignments_and_checkout_overhaul.md +0 -90
  56. package/resources/local/codex/memories/rollout_summaries/2026-04-25T03-39-02-mbDr-web_book_refund_admin_popup_pagination_responsiveness.md +0 -151
  57. package/resources/local/codex/memories/rollout_summaries/2026-04-25T09-20-30-4usS-tool_scv_9router_custom_provider_and_paddle_ocr.md +0 -130
  58. package/resources/local/codex/memories/rollout_summaries/2026-05-06T10-19-38-mt2X-find_db_config_in_web_book_app_env.md +0 -40
  59. package/resources/local/codex/memories/rollout_summaries/2026-05-06T11-10-23-TkwP-goirong_backend_title_crash_and_client_audio_tcp_tunnel_debu.md +0 -85
  60. package/resources/local/codex/memories/rollout_summaries/2026-05-09T07-52-18-On1F-chakra_git_cleanup_readme_bilingual_publish_config.md +0 -88
  61. package/resources/local/codex/memories/rollout_summaries/2026-05-11T08-05-34-oMEl-check_crack_gui_logo_onefile_build.md +0 -68
  62. package/resources/local/codex/memories/skills/windows-packaged-app-smoke-check/SKILL.md +0 -72
package/src/cli/repl.js CHANGED
@@ -7,6 +7,7 @@ import readline from 'readline';
7
7
  import { promises as fs } from 'fs';
8
8
  import { homedir } from 'os';
9
9
  import { welcomeBanner, colors } from './snowflake-logo.js';
10
+ import { renderBox, terminalWidth, stripAnsi, visibleWidth, wrapText, padVisible } from './terminal-ui.js';
10
11
  import { ToolExecutor } from '../tools/executor.js';
11
12
  import { SessionManager } from '../session/manager.js';
12
13
  import { AIProviderManager } from '../ai/providers.js';
@@ -84,12 +85,16 @@ function formatMarkdown(text) {
84
85
  // Fallback
85
86
  }
86
87
 
87
- const columns = process.stdout.columns || 80;
88
- const W = Math.max(60, Math.min(Math.floor(columns * 0.95), 100));
89
- const headerLine = '─'.repeat(Math.max(0, W - label.length - 4));
90
- const bottomLine = '─'.repeat(W);
88
+ const boxWidth = Math.max(60, Math.min(terminalWidth(60, 100, 80), 100));
89
+ const body = wrapText(coloredCode, boxWidth - 4);
91
90
 
92
- return `\n${colors.dim}┌─ ${label} ${headerLine}${colors.reset}\n${coloredCode}\n${colors.dim}└${bottomLine}${colors.reset}\n${colors.white}`;
91
+ return `\n${renderBox({
92
+ title: label,
93
+ width: boxWidth,
94
+ borderColor: colors.dim,
95
+ titleColor: colors.dim,
96
+ body,
97
+ })}\n${colors.white}`;
93
98
  });
94
99
 
95
100
  // Bold
@@ -164,51 +169,106 @@ export class WinterREPL {
164
169
  this.isCancelled = false;
165
170
  }
166
171
 
172
+ getProjectInstructionFiles() {
173
+ return ['winter.md', 'WINTER.md', 'CLAUDE.md', '.claude/CLAUDE.md'];
174
+ }
175
+
176
+ async readProjectInstructionFiles() {
177
+ const fsPromises = await import('fs/promises');
178
+ const files = [];
179
+ const seen = new Set();
180
+
181
+ for (const relativePath of this.getProjectInstructionFiles()) {
182
+ const filePath = path.join(this.projectPath, relativePath);
183
+ const normalizedPath = path.normalize(filePath).toLowerCase();
184
+
185
+ if (seen.has(normalizedPath)) continue;
186
+ seen.add(normalizedPath);
187
+
188
+ try {
189
+ const stat = await fsPromises.stat(filePath).catch(() => null);
190
+ if (!stat || !stat.isFile()) continue;
191
+
192
+ const content = await fsPromises.readFile(filePath, 'utf8');
193
+ files.push({ relativePath, filePath, content });
194
+ } catch {
195
+ // Ignore unreadable instruction files.
196
+ }
197
+ }
198
+
199
+ return files;
200
+ }
201
+
167
202
  async start() {
168
203
  await this.session.init({ project: this.projectPath, sessionId: this.sessionId });
169
204
  await this.ai.init();
170
205
 
171
- // Tự động đọc và ghi nhớ 3 file hệ thống theo yêu cầu của user
172
- const filesToAutoLoad = [
173
- 'e:\\\\dev\\\\app\\\\winter\\\\resources\\\\local\\\\agents.md',
174
- 'e:\\\\dev\\\\app\\\\winter\\\\resources\\\\local\\\\awesome-design-md',
175
- 'e:\\\\dev\\\\app\\\\winter\\\\resources\\\\local\\\\karpathy-tools'
176
- ];
206
+ await this.session.updateContext('projectAnchor', {
207
+ path: this.projectPath,
208
+ name: path.basename(this.projectPath),
209
+ openedAt: new Date().toISOString(),
210
+ });
211
+ await this.session.replaceMemory('[Project Anchor]', `Current project is ${this.projectPath}. Treat this path as the canonical working directory for the session.`, 'info');
177
212
 
213
+ // Tự động đọc và ghi nhớ một số tài nguyên cục bộ (an toàn): chỉ nạp file hoặc README trong thư mục
178
214
  const fsPromises = await import('fs/promises');
179
- const path = await import('path');
215
+ const resourcePaths = this.getResourcePaths();
216
+ const autoLoadTargets = [resourcePaths.agents, resourcePaths.designs, resourcePaths.karpathy];
180
217
 
181
- for (const filePath of filesToAutoLoad) {
218
+ for (const targetPath of autoLoadTargets) {
182
219
  try {
183
- const content = await fsPromises.readFile(filePath, 'utf8');
184
- const fileName = path.basename(filePath);
185
- const memoryKey = `[Tự động ghi nhớ file ${fileName}]`;
220
+ const stat = await fsPromises.stat(targetPath).catch(() => null);
221
+ if (!stat) continue;
186
222
 
187
- // Xóa memory cũ về file này để cập nhật nội dung mới nhất (tránh trùng lặp)
188
- const currentMemories = this.session.getMemory() || [];
189
- const filteredMemories = currentMemories.filter(m => !m.startsWith(memoryKey));
223
+ if (stat.isFile()) {
224
+ const content = await fsPromises.readFile(targetPath, 'utf8');
225
+ const fileName = path.basename(targetPath);
226
+ const memoryKey = `[Tự động ghi nhớ file ${fileName}]`;
227
+ await this.session.replaceMemory(memoryKey, content);
228
+ console.log(`${colors.dim}ℹ Đã tự động nạp và ghi nhớ file ${fileName}${colors.reset}`);
229
+ continue;
230
+ }
190
231
 
191
- // Cập nhật lại mảng memory trong session
192
- this.session.memory = filteredMemories;
232
+ if (stat.isDirectory()) {
233
+ // Try README.md, index.md, or manifest.json inside the directory
234
+ const candidates = ['README.md', 'README.MD', 'index.md', 'manifest.json'];
235
+ let loaded = false;
236
+ for (const c of candidates) {
237
+ const p = path.join(targetPath, c);
238
+ try {
239
+ const cstat = await fsPromises.stat(p).catch(() => null);
240
+ if (cstat && cstat.isFile()) {
241
+ const content = await fsPromises.readFile(p, 'utf8');
242
+ const memoryKey = `[Tự động ghi nhớ file ${path.basename(targetPath)}/${c}]`;
243
+ await this.session.replaceMemory(memoryKey, content);
244
+ console.log(`${colors.dim}ℹ Đã tự động nạp và ghi nhớ ${path.basename(targetPath)}/${c}${colors.reset}`);
245
+ loaded = true;
246
+ break;
247
+ }
248
+ } catch (e) {
249
+ // continue
250
+ }
251
+ }
193
252
 
194
- // Thêm nội dung mới vào bộ nhớ
195
- this.session.addMemory(`${memoryKey}:\n${content}`);
196
- console.log(`${colors.dim}ℹ Đã tự động nạp và ghi nhớ file ${fileName}${colors.reset}`);
253
+ if (!loaded) {
254
+ // nothing to load
255
+ }
256
+ }
197
257
  } catch (e) {
198
- // Bỏ qua nếu không đọc được file
258
+ // Ignore read errors for resources
199
259
  }
200
260
  }
201
261
 
202
- // Kiểm tra và nạp file winter.md của dự án
203
- const projectWinterMd = path.join(this.projectPath, 'winter.md');
262
+ // Nạp các file quy tắc dự án theo thứ tự ưu tiên.
263
+ const projectInstructionFiles = await this.readProjectInstructionFiles();
204
264
  try {
205
- await fsPromises.access(projectWinterMd);
206
- const content = await fsPromises.readFile(projectWinterMd, 'utf8');
207
-
208
- const memoryKey = `[Quy tắc dự án từ winter.md]`;
209
- this.session.memory = (this.session.getMemory() || []).filter(m => !m.startsWith(memoryKey));
210
- this.session.addMemory(`${memoryKey}:\n${content}`);
211
- console.log(`${colors.dim}ℹ Đã nạp quy tắc dự án từ winter.md${colors.reset}`);
265
+ if (projectInstructionFiles.length > 0) {
266
+ for (const file of projectInstructionFiles) {
267
+ const memoryKey = `[Quy tắc dự án từ ${file.relativePath}]`;
268
+ await this.session.replaceMemory(memoryKey, file.content);
269
+ console.log(`${colors.dim}ℹ Đã nạp quy tắc dự án từ ${file.relativePath}${colors.reset}`);
270
+ }
271
+ }
212
272
  } catch (e) {
213
273
  // Nếu không có, tự động tạo file mẫu!
214
274
  const template = `# Winter Project Rules
@@ -243,17 +303,20 @@ export class WinterREPL {
243
303
  - Luôn đảm bảo file không bị lỗi cú pháp sau khi sửa.
244
304
  `;
245
305
  try {
306
+ const projectWinterMd = path.join(this.projectPath, 'winter.md');
246
307
  await fsPromises.writeFile(projectWinterMd, template, 'utf8');
247
308
  console.log(`\n${colors.green}✓ Đã tự động tạo file winter.md mẫu cho dự án mới!${colors.reset}`);
248
309
  console.log(`${colors.dim}Bạn có thể chỉnh sửa file này để dạy AI các quy tắc riêng của dự án.${colors.reset}\n`);
249
310
 
250
311
  // Nạp luôn vào memory
251
- this.session.addMemory(`[Quy tắc dự án từ winter.md]:\n${template}`);
312
+ await this.session.replaceMemory(`[Quy tắc dự án từ winter.md]`, template);
252
313
  } catch (err) {
253
314
  // Bỏ qua nếu không ghi được file
254
315
  }
255
316
  }
256
317
 
318
+ await this.bootstrapProjectCapabilities();
319
+
257
320
  const activeProvider = this.ai.getActiveProvider();
258
321
  const info = {
259
322
  project: this.projectPath,
@@ -1109,48 +1172,50 @@ export class WinterREPL {
1109
1172
 
1110
1173
  showCommandMenu() {
1111
1174
  const c = colors;
1112
- const W = 78;
1113
- const line = '─'.repeat(W - 2);
1114
- const dline = '═'.repeat(W - 2);
1115
- const row = (l, r) => {
1116
- const ll = l.replace(/\x1b\[[0-9;]*m/g, '');
1117
- const rl = r.replace(/\x1b\[[0-9;]*m/g, '');
1118
- const pad = W - 4 - ll.length - rl.length;
1119
- return `${c.magenta}│${c.reset} ${l}${' '.repeat(Math.max(1, pad))}${r} ${c.magenta}│${c.reset}`;
1120
- };
1121
- const header = (text) => {
1122
- const tl = text.replace(/\x1b\[[0-9;]*m/g, '').length;
1123
- const pad = W - 4 - tl;
1124
- return `${c.magenta}│${c.reset} ${text}${' '.repeat(Math.max(0, pad))} ${c.magenta}│${c.reset}`;
1175
+ const width = terminalWidth(72, 112, 92);
1176
+ const innerWidth = width - 4;
1177
+ const split = Math.floor(innerWidth * 0.54);
1178
+ const rightWidth = innerWidth - split - 1;
1179
+ const row = (left, right = '') => {
1180
+ if (!right) return left;
1181
+ return `${padVisible(left, split)} ${padVisible(right, rightWidth)}`;
1125
1182
  };
1126
- const sep = `${c.magenta}├${line}┤${c.reset}`;
1183
+
1184
+ const body = [
1185
+ `${c.bright}${c.cyan}❄ WINTER COMMANDS${c.reset}`,
1186
+ '',
1187
+ `${c.bright}Dự án & Phiên làm việc${c.reset}`,
1188
+ row(`${c.yellow}/pwd${c.reset} Thư mục hiện tại`, `${c.yellow}/session${c.reset} Phiên làm việc`),
1189
+ row(`${c.yellow}/cd${c.reset} Đổi thư mục`, `${c.yellow}/clear${c.reset} Xóa màn hình`),
1190
+ row(`${c.yellow}/config${c.reset} Xem cấu hình`, `${c.yellow}/exit${c.reset} Thoát`),
1191
+ '',
1192
+ `${c.bright}AI & Công cụ${c.reset}`,
1193
+ row(`${c.yellow}/auto${c.reset} TDD tự sửa lỗi`, `${c.yellow}/agent${c.reset} Chạy sub-agent`),
1194
+ row(`${c.yellow}/read${c.reset} Đọc file`, `${c.yellow}/write${c.reset} Ghi file`),
1195
+ row(`${c.yellow}/bash${c.reset} Chạy lệnh terminal`, `${c.yellow}/grep${c.reset} Tìm trong file`),
1196
+ row(`${c.yellow}/glob${c.reset} Tìm file theo pattern`, `${c.yellow}/image${c.reset} Phân tích UI`),
1197
+ row(`${c.yellow}/paste${c.reset} Dán từ clipboard`, `${c.yellow}/plan${c.reset} Lập kế hoạch`),
1198
+ '',
1199
+ `${c.bright}Git Auto-Pilot${c.reset}`,
1200
+ row(`${c.yellow}/commit${c.reset} AI tự viết commit`, `${c.yellow}/review${c.reset} AI review code thay đổi`),
1201
+ '',
1202
+ `${c.bright}Cấu hình Model${c.reset}`,
1203
+ row(`${c.yellow}/provider${c.reset} Đổi provider AI`, `${c.yellow}/model${c.reset} Đổi model`),
1204
+ row(`${c.yellow}/providers${c.reset} Danh sách provider`, `${c.yellow}/models${c.reset} Danh sách model`),
1205
+ '',
1206
+ `${c.bright}Bộ nhớ & Kỹ năng${c.reset}`,
1207
+ row(`${c.yellow}/remember${c.reset} Lưu vào bộ nhớ`, `${c.yellow}/memories${c.reset} Xem bộ nhớ`),
1208
+ row(`${c.yellow}/skills${c.reset} Danh sách kỹ năng`, `${c.yellow}/designs${c.reset} Hệ thống thiết kế`),
1209
+ ];
1210
+
1127
1211
  console.log(`
1128
- ${c.magenta}╭${line}╮${c.reset}
1129
- ${header(`${c.bright}${c.cyan}WINTER COMMANDS${c.reset}`)}
1130
- ${sep}
1131
- ${header(`${c.bright}Dự án & Phiên làm việc${c.reset}`)}
1132
- ${row(`${c.yellow}/pwd${c.reset} Thư mục hiện tại`, `${c.yellow}/session${c.reset} Phiên làm việc`)}
1133
- ${row(`${c.yellow}/cd${c.reset} Đổi thư mục`, `${c.yellow}/clear${c.reset} Xóa màn hình`)}
1134
- ${row(`${c.yellow}/config${c.reset} Xem cấu hình`, `${c.yellow}/exit${c.reset} Thoát`)}
1135
- ${sep}
1136
- ${header(`${c.bright}AI & Công cụ${c.reset}`)}
1137
- ${row(`${c.yellow}/auto${c.reset} TDD tự sửa lỗi`, `${c.yellow}/agent${c.reset} Chạy sub-agent`)}
1138
- ${row(`${c.yellow}/read${c.reset} Đọc file`, `${c.yellow}/write${c.reset} Ghi file`)}
1139
- ${row(`${c.yellow}/bash${c.reset} Chạy lệnh terminal`, `${c.yellow}/grep${c.reset} Tìm trong file`)}
1140
- ${row(`${c.yellow}/glob${c.reset} Tìm file theo pattern`, `${c.yellow}/image${c.reset} Phân tích UI`)}
1141
- ${row(`${c.yellow}/paste${c.reset} Dán từ clipboard`, `${c.yellow}/plan${c.reset} Lập kế hoạch`)}
1142
- ${sep}
1143
- ${header(`${c.bright}Git Auto-Pilot${c.reset}`)}
1144
- ${row(`${c.yellow}/commit${c.reset} AI tự viết commit`, `${c.yellow}/review${c.reset} AI review code thay đổi`)}
1145
- ${sep}
1146
- ${header(`${c.bright}Cấu hình Model${c.reset}`)}
1147
- ${row(`${c.yellow}/provider${c.reset} Đổi provider AI`, `${c.yellow}/model${c.reset} Đổi model`)}
1148
- ${row(`${c.yellow}/providers${c.reset} Danh sách provider`, `${c.yellow}/models${c.reset} Danh sách model`)}
1149
- ${sep}
1150
- ${header(`${c.bright}Bộ nhớ & Kỹ năng${c.reset}`)}
1151
- ${row(`${c.yellow}/remember${c.reset} Lưu vào bộ nhớ`, `${c.yellow}/memories${c.reset} Xem bộ nhớ`)}
1152
- ${row(`${c.yellow}/skills${c.reset} Danh sách kỹ năng`, `${c.yellow}/designs${c.reset} Hệ thống thiết kế`)}
1153
- ${c.magenta}╰${line}╯${c.reset}
1212
+ ${renderBox({
1213
+ title: `${c.bright}${c.cyan}WINTER COMMANDS${c.reset}`,
1214
+ width,
1215
+ borderColor: c.magenta,
1216
+ titleColor: c.cyan,
1217
+ body,
1218
+ })}
1154
1219
  ${c.dim}Gõ tin nhắn trực tiếp để chat · ESC để hủy · Prompt tự xếp hàng chờ${c.reset}
1155
1220
  `);
1156
1221
  }
@@ -1358,13 +1423,15 @@ ${colors.reset}
1358
1423
  usedTools = true;
1359
1424
  if (this.spinner) this.spinner.stop();
1360
1425
 
1361
- const BOX_WIDTH = 80;
1362
- const topLabel = ' AGENT TOOLS EXECUTION ';
1363
- const topLeft = '╭─';
1364
- const topRight = '─╮';
1365
- const topPadLen = BOX_WIDTH - topLeft.length - topRight.length - topLabel.length;
1366
- const topLine = `${topLeft}${colors.bright}${topLabel}${colors.reset}${colors.magenta}${'─'.repeat(Math.max(0, topPadLen))}${topRight}`;
1367
- console.log(`\n${colors.magenta}${topLine}${colors.reset}`);
1426
+ const BOX_WIDTH = terminalWidth(76, 116, 92);
1427
+ const innerWidth = BOX_WIDTH - 2;
1428
+ const title = ' AGENT TOOLS EXECUTION ';
1429
+ const titlePad = Math.max(0, innerWidth - visibleWidth(title));
1430
+ const leftPad = Math.floor(titlePad / 2);
1431
+ const rightPad = titlePad - leftPad;
1432
+ console.log(`\n${colors.magenta}╭${'─'.repeat(innerWidth)}╮${colors.reset}`);
1433
+ console.log(`${colors.magenta}│${colors.reset}${' '.repeat(leftPad)}${colors.bright}${title}${colors.reset}${' '.repeat(rightPad)}${colors.magenta}│${colors.reset}`);
1434
+ console.log(`${colors.magenta}├${'─'.repeat(innerWidth)}┤${colors.reset}`);
1368
1435
  messages.push({
1369
1436
  role: 'assistant',
1370
1437
  content: assistantMsg.content || '',
@@ -1420,34 +1487,16 @@ ${colors.reset}
1420
1487
  const statusIcon = result.success === false ? `${colors.red}✖${colors.reset}` : `${colors.green}✓${colors.reset}`;
1421
1488
 
1422
1489
  const maxLen = BOX_WIDTH - 8;
1423
- const lines = summary.split('\n');
1424
- for (const line of lines) {
1425
- // Loại bỏ màu ANSI để tính toán độ dài và cắt dòng chuẩn xác
1426
- const cleanLine = line.replace(/\x1b\[[0-9;]*m/g, '');
1427
-
1428
- if (cleanLine.length <= maxLen) {
1429
- const padLen = BOX_WIDTH - 7 - cleanLine.length;
1430
- const padding = ' '.repeat(Math.max(0, padLen));
1431
- console.log(`${colors.magenta}│${colors.reset} ${statusIcon} ${colors.dim}${cleanLine}${colors.reset}${padding}${colors.magenta}│${colors.reset}`);
1432
- } else {
1433
- // Word wrap
1434
- let remaining = cleanLine;
1435
- let first = true;
1436
- while (remaining.length > 0) {
1437
- const chunk = remaining.substring(0, maxLen);
1438
- remaining = remaining.substring(maxLen);
1439
- const prefix = first ? statusIcon : ' ';
1440
-
1441
- const padLen = BOX_WIDTH - 7 - chunk.length;
1442
- const padding = ' '.repeat(Math.max(0, padLen));
1443
- console.log(`${colors.magenta}│${colors.reset} ${prefix} ${colors.dim}${chunk}${colors.reset}${padding}${colors.magenta}│${colors.reset}`);
1444
- first = false;
1445
- }
1446
- }
1490
+ const wrappedLines = summary.split('\n').flatMap(line => wrapText(line, maxLen));
1491
+ for (const [index, line] of wrappedLines.entries()) {
1492
+ const prefix = index === 0 ? statusIcon : ' ';
1493
+ const cleanLine = stripAnsi(line);
1494
+ const padding = ' '.repeat(Math.max(0, maxLen - visibleWidth(cleanLine)));
1495
+ console.log(`${colors.magenta}│${colors.reset} ${prefix} ${colors.dim}${cleanLine}${colors.reset}${padding}${colors.magenta}│${colors.reset}`);
1447
1496
  }
1448
1497
  }
1449
1498
  }
1450
- console.log(`${colors.magenta}╰${'─'.repeat(BOX_WIDTH - 1)}╯${colors.reset}\n`);
1499
+ console.log(`${colors.magenta}╰${'─'.repeat(innerWidth)}╯${colors.reset}\n`);
1451
1500
  }
1452
1501
 
1453
1502
  if (usedTools && !finalContent) {
@@ -2079,16 +2128,16 @@ ${colors.reset}
2079
2128
 
2080
2129
  async chat(message, imageAttachments = []) {
2081
2130
  try {
2082
- const needsTools = this.shouldUseTools(message, imageAttachments);
2083
- const context = needsTools ? await this.getProjectContext() : '';
2131
+ const needsTools = true;
2132
+ const context = await this.getProjectContext();
2084
2133
  const messages = [
2085
- { role: 'system', content: needsTools ? this.getSystemPrompt(context) : this.getFastSystemPrompt() }
2134
+ { role: 'system', content: this.getSystemPrompt(context) }
2086
2135
  ];
2087
2136
 
2088
2137
  const history = this.getPromptHistory({
2089
- limit: needsTools ? 20 : 4,
2090
- maxEntryChars: needsTools ? 2000 : 350,
2091
- maxTotalChars: needsTools ? 12000 : 900,
2138
+ limit: 20,
2139
+ maxEntryChars: 2000,
2140
+ maxTotalChars: 12000,
2092
2141
  });
2093
2142
  for (const entry of history) {
2094
2143
  messages.push({ role: entry.role, content: entry.content });
@@ -2109,7 +2158,7 @@ ${colors.reset}
2109
2158
  messages.push({ role: 'user', content: message });
2110
2159
  }
2111
2160
 
2112
- const tools = needsTools ? this.getAgentTools('general') : [];
2161
+ const tools = this.getAgentTools('general');
2113
2162
  const finalContent = await this.runConversation(messages, 'Thinking', tools);
2114
2163
 
2115
2164
  await this.session.addToHistory({ role: 'user', content: message });
@@ -2121,12 +2170,7 @@ ${colors.reset}
2121
2170
  }
2122
2171
 
2123
2172
  shouldUseTools(message = '', imageAttachments = []) {
2124
- if (imageAttachments.length > 0) return false;
2125
- const text = String(message || '').toLowerCase();
2126
- if (/[a-z]:[\\/]|\.([cm]?[jt]sx?|json|md|css|html|py|java|go|rs|php|rb|toml|ya?ml)\b/i.test(text)) {
2127
- return true;
2128
- }
2129
- return /\b(read|write|edit|file|folder|repo|project|code|bug|fix|debug|test|build|run|git|commit|push|pull|npm|node|install|create|delete|copy|move|refactor|grep|glob|bash|terminal|powershell|deploy)\b|sửa|lỗi|đọc|thư mục|dự án|mã|kiểm tra|chạy|tạo|xóa|giao diện|ảnh|màn hình|đẩy|cài|build|test/i.test(text);
2173
+ return true;
2130
2174
  }
2131
2175
 
2132
2176
  getPromptHistory({ limit = 20, maxEntryChars = 2000, maxTotalChars = 12000 } = {}) {
@@ -2171,19 +2215,29 @@ ${colors.reset}
2171
2215
 
2172
2216
  async getProjectContext() {
2173
2217
  const context = [];
2174
- const projectFiles = ['CLAUDE.md', 'WINTER.md', '.claude/CLAUDE.md', 'package.json'];
2218
+ const projectInstructionFiles = await this.readProjectInstructionFiles();
2175
2219
 
2176
- for (const file of projectFiles) {
2220
+ for (const file of projectInstructionFiles) {
2177
2221
  try {
2178
- const filePath = path.join(this.projectPath, file);
2179
- const stat = await fs.stat(filePath);
2180
- if (stat.isFile()) {
2181
- const content = await fs.readFile(filePath, 'utf-8');
2182
- context.push(`[${file}]\n${content.substring(0, 300)}...`);
2183
- }
2222
+ const preview = this.compactText(file.content, 900, 'project instruction');
2223
+ context.push(`[${file.relativePath}]\n${preview}`);
2184
2224
  } catch { }
2185
2225
  }
2186
2226
 
2227
+ try {
2228
+ const packageJsonPath = path.join(this.projectPath, 'package.json');
2229
+ const stat = await fs.stat(packageJsonPath);
2230
+ if (stat.isFile()) {
2231
+ const content = await fs.readFile(packageJsonPath, 'utf-8');
2232
+ context.push(`[package.json]\n${this.compactText(content, 1200, 'package.json')}`);
2233
+ }
2234
+ } catch { }
2235
+
2236
+ const localResources = await this.getLocalResourceContext();
2237
+ if (localResources) {
2238
+ context.push(localResources);
2239
+ }
2240
+
2187
2241
  // Git Context
2188
2242
  try {
2189
2243
  const { execSync } = await import('child_process');
@@ -2191,35 +2245,240 @@ ${colors.reset}
2191
2245
  if (gitStatus) {
2192
2246
  context.push(`[Git Status]\n${gitStatus}`);
2193
2247
 
2248
+ const gitSummary = execSync('git diff --stat --summary', { cwd: this.projectPath, encoding: 'utf8', stdio: 'pipe', maxBuffer: 1024 * 50 }).trim();
2249
+ if (gitSummary) {
2250
+ context.push(`[Git Summary]\n${this.compactText(gitSummary, 1200, 'git summary')}`);
2251
+ }
2252
+
2194
2253
  // Get brief git diff for context
2195
- const gitDiff = execSync('git diff', { cwd: this.projectPath, encoding: 'utf8', stdio: 'pipe', maxBuffer: 1024 * 50 }).trim().split('\n').slice(0, 50).join('\n');
2254
+ const gitDiff = execSync('git diff', { cwd: this.projectPath, encoding: 'utf8', stdio: 'pipe', maxBuffer: 1024 * 50 }).trim().split('\n').slice(0, 30).join('\n');
2196
2255
  if (gitDiff) {
2197
- context.push(`[Git Diff]\n${gitDiff}\n...`);
2256
+ context.push(`[Git Diff]\n${this.compactText(gitDiff, 1800, 'git diff')}`);
2198
2257
  }
2199
2258
  }
2200
2259
  } catch (e) {
2201
2260
  // Not a git repo or git not installed
2202
2261
  }
2203
2262
 
2204
- return context.join('\n\n') || 'No project context found.';
2263
+ return this.compactText(context.join('\n\n') || 'No project context found.', 9000, 'project context');
2264
+ }
2265
+
2266
+ async getLocalResourceContext() {
2267
+ try {
2268
+ const manifestPath = this.getResourcePaths().manifest;
2269
+ const raw = await fs.readFile(manifestPath, 'utf8');
2270
+ const manifest = JSON.parse(raw.replace(/^\uFEFF/, ''));
2271
+ const paths = this.getResourcePaths();
2272
+ const [claudeSkills, codexSkills, claudePlugins, codexMemories] = await Promise.all([
2273
+ this.listPathEntries(paths.claude.skills, 20),
2274
+ this.listPathEntries(paths.codex.skills, 20),
2275
+ this.listPathEntries(paths.claude.plugins, 20),
2276
+ this.listPathEntries(paths.codex.memories, 20),
2277
+ ]);
2278
+
2279
+ const lines = [];
2280
+ lines.push('[Local Resources]');
2281
+ lines.push(`- Root: ${manifest.root || this.getResourcePaths().localRoot}`);
2282
+
2283
+ for (const resource of manifest.localResources || []) {
2284
+ lines.push(`- ${resource.name}: ${resource.files} files, ${(resource.bytes / 1024 / 1024).toFixed(2)} MB`);
2285
+ }
2286
+
2287
+ if (manifest.redacted?.length) {
2288
+ lines.push(`- Redacted: ${manifest.redacted.join('; ')}`);
2289
+ }
2290
+
2291
+ lines.push('- Use Read/Grep/Glob to inspect any local resource when it matters for the task.');
2292
+ lines.push('- Local resource families: agents.md, awesome-design-md, claude, codex, karpathy-tools.');
2293
+
2294
+ if (claudeSkills.length > 0) {
2295
+ lines.push(`- Claude skills: ${claudeSkills.slice(0, 10).map(item => item.name).join(', ')}${claudeSkills.length > 10 ? ', ...' : ''}`);
2296
+ }
2297
+
2298
+ if (claudePlugins.length > 0) {
2299
+ lines.push(`- Claude plugin roots: ${claudePlugins.slice(0, 10).map(item => item.name).join(', ')}${claudePlugins.length > 10 ? ', ...' : ''}`);
2300
+ }
2301
+
2302
+ if (codexSkills.length > 0) {
2303
+ lines.push(`- Codex skills: ${codexSkills.slice(0, 10).map(item => item.name).join(', ')}${codexSkills.length > 10 ? ', ...' : ''}`);
2304
+ }
2305
+
2306
+ if (codexMemories.length > 0) {
2307
+ lines.push(`- Codex memories: ${codexMemories.slice(0, 10).map(item => item.name).join(', ')}${codexMemories.length > 10 ? ', ...' : ''}`);
2308
+ }
2309
+
2310
+ return lines.join('\n');
2311
+ } catch {
2312
+ return '';
2313
+ }
2314
+ }
2315
+
2316
+ async bootstrapProjectCapabilities() {
2317
+ const sessionContext = this.session.getContext() || {};
2318
+
2319
+ if (!sessionContext.bootstrapPlan?.id && this.session.getPlans().length === 0) {
2320
+ const plan = await this.session.createPlan(
2321
+ 'Bootstrap project context',
2322
+ 'Inspect rules, resources, and likely skills before doing any task work.'
2323
+ );
2324
+ await this.session.addPlanStep(plan.id, {
2325
+ description: 'Read project rules, local resources, and attached skill libraries.',
2326
+ });
2327
+ await this.session.addPlanStep(plan.id, {
2328
+ description: 'Choose the smallest relevant skill set before making changes.',
2329
+ });
2330
+ await this.session.updateContext('bootstrapPlan', {
2331
+ id: plan.id,
2332
+ title: plan.title,
2333
+ description: plan.description,
2334
+ });
2335
+ }
2336
+
2337
+ const skillSnapshot = await this.inferStartupSkills();
2338
+ await this.session.updateContext('availableSkillCatalog', skillSnapshot.availableSkills);
2339
+ await this.session.updateContext('activeSkills', skillSnapshot.activeSkills);
2340
+
2341
+ const appliedText = skillSnapshot.activeSkills.length > 0
2342
+ ? `Auto-applied skills: ${skillSnapshot.activeSkills.join(', ')}`
2343
+ : 'Auto-applied skills: none';
2344
+ await this.session.replaceMemory('[Auto-applied skills]', appliedText, 'skill');
2345
+ }
2346
+
2347
+ async inferStartupSkills() {
2348
+ const catalog = await this.getStartupSkillCatalog();
2349
+ const signals = await this.getProjectSignals();
2350
+ const normalizedSignals = new Set(signals.map(value => value.toLowerCase()));
2351
+
2352
+ const hasAny = (...items) => items.some(item => normalizedSignals.has(item));
2353
+ const activeSkills = new Set([
2354
+ 'coding',
2355
+ 'debug',
2356
+ 'refactor',
2357
+ 'test',
2358
+ ]);
2359
+
2360
+ if (hasAny('react', 'next', 'nextjs', 'tsx', 'jsx', 'vue', 'svelte', 'vite')) {
2361
+ ['vercel-react-best-practices', 'web-design-guidelines', 'frontend-design', 'design'].forEach(skill => activeSkills.add(skill));
2362
+ }
2363
+
2364
+ if (hasAny('design', 'ui', 'ux', 'css', 'tailwind', 'styled-components', 'scss', 'style', 'component')) {
2365
+ ['web-design-guidelines', 'frontend-design', 'design'].forEach(skill => activeSkills.add(skill));
2366
+ }
2367
+
2368
+ if (hasAny('claude', 'agent', 'mcp', 'plugin', 'skill', 'automation', 'workflow')) {
2369
+ ['skill-creator', 'claude-automation-recommender', 'claude-md-improver', 'agent-development', 'hook-development', 'command-development', 'plugin-dev'].forEach(skill => activeSkills.add(skill));
2370
+ }
2371
+
2372
+ if (hasAny('docs', 'markdown', 'md', 'readme', 'documentation')) {
2373
+ ['claude-md-improver', 'docs', 'writing-rules'].forEach(skill => activeSkills.add(skill));
2374
+ }
2375
+
2376
+ if (hasAny('figma', 'design-md', 'brand', 'brand-guidelines', 'style-guide')) {
2377
+ ['vibefigma', 'web-design-guidelines'].forEach(skill => activeSkills.add(skill));
2378
+ }
2379
+
2380
+ const filtered = [...activeSkills].filter(skill => catalog.has(skill));
2381
+ return {
2382
+ availableSkills: [...catalog],
2383
+ activeSkills: filtered,
2384
+ };
2385
+ }
2386
+
2387
+ async getStartupSkillCatalog() {
2388
+ const catalog = new Set(['coding', 'design', 'debug', 'refactor', 'test', 'security', 'performance']);
2389
+ const resourcePaths = this.getResourcePaths();
2390
+ const folders = [resourcePaths.claude.skills, resourcePaths.codex.skills];
2391
+
2392
+ for (const folder of folders) {
2393
+ const entries = await this.listPathEntries(folder, 200);
2394
+ for (const entry of entries) {
2395
+ catalog.add(entry.name);
2396
+ }
2397
+ }
2398
+
2399
+ return catalog;
2400
+ }
2401
+
2402
+ async getProjectSignals() {
2403
+ const signals = [];
2404
+
2405
+ try {
2406
+ const packageJsonPath = path.join(this.projectPath, 'package.json');
2407
+ const raw = await fs.readFile(packageJsonPath, 'utf8');
2408
+ const pkg = JSON.parse(raw);
2409
+
2410
+ signals.push(String(pkg.name || '').toLowerCase());
2411
+ signals.push(String(pkg.description || '').toLowerCase());
2412
+
2413
+ for (const key of ['dependencies', 'devDependencies', 'peerDependencies']) {
2414
+ const deps = pkg[key] || {};
2415
+ for (const depName of Object.keys(deps)) {
2416
+ signals.push(depName.toLowerCase());
2417
+ }
2418
+ }
2419
+
2420
+ for (const script of Object.values(pkg.scripts || {})) {
2421
+ signals.push(String(script).toLowerCase());
2422
+ }
2423
+ } catch {
2424
+ // Ignore package.json parsing issues.
2425
+ }
2426
+
2427
+ try {
2428
+ const entries = await fs.readdir(this.projectPath, { withFileTypes: true });
2429
+ for (const entry of entries) {
2430
+ if (!entry.isFile()) continue;
2431
+ signals.push(path.extname(entry.name).toLowerCase().slice(1));
2432
+ signals.push(entry.name.toLowerCase());
2433
+ }
2434
+ } catch {
2435
+ // Ignore directory scan issues.
2436
+ }
2437
+
2438
+ return signals.filter(Boolean);
2205
2439
  }
2206
2440
 
2207
2441
  getSystemPrompt(context = '') {
2208
2442
  const memories = this.session.getMemory();
2209
2443
  const plans = this.session.getPlans();
2210
-
2211
- let memoryStr = memories.length > 0 ? `\n## Memories (Important Context)\n${memories.map(m => `- ${m.text}`).join('\n')}` : '';
2212
- let plansStr = plans.length > 0 ? `\n## Active Plans & Tasks\n${plans.map(p => `- [${p.status}] ${p.title}: ${p.description}`).join('\n')}` : '';
2444
+ const sessionContext = this.session.getContext() || {};
2445
+
2446
+ let memoryStr = memories.length > 0 ? `\n## Memories (Important Context)\n${this.summarizePromptList(memories, {
2447
+ limit: 8,
2448
+ maxEntryChars: 220,
2449
+ maxTotalChars: 1600,
2450
+ mapper: memory => memory.text,
2451
+ })}` : '';
2452
+ let plansStr = plans.length > 0 ? `\n## Active Plans & Tasks\n${this.summarizePromptList(plans, {
2453
+ limit: 6,
2454
+ maxEntryChars: 260,
2455
+ maxTotalChars: 1600,
2456
+ mapper: plan => `[${plan.status}] ${plan.title}: ${plan.description}`,
2457
+ })}` : '';
2458
+ let skillsStr = Array.isArray(sessionContext.activeSkills) && sessionContext.activeSkills.length > 0
2459
+ ? `\n## Auto-applied Skills\n${sessionContext.activeSkills.slice(0, 12).map(skill => `- ${skill}`).join('\n')}${sessionContext.activeSkills.length > 12 ? '\n- ...' : ''}`
2460
+ : '';
2461
+ let startupPlanStr = sessionContext.bootstrapPlan?.title
2462
+ ? `\n## Startup Plan\n- ${sessionContext.bootstrapPlan.title}: ${sessionContext.bootstrapPlan.description}`
2463
+ : '';
2213
2464
 
2214
2465
  return `You are Winter, an expert AI coding assistant.
2215
2466
 
2216
2467
  ## CRITICAL AI RULES (MUST FOLLOW STRICTLY):
2217
- 1. [THINKING BEFORE CODING]: Always output your thought process briefly before generating code. Think about edge cases, design structure, and syntax correctness.
2468
+ 1. [THINKING BEFORE CODING]: State assumptions, constraints, and a brief plan before making changes. Be thorough enough to be useful, and do not invent facts.
2218
2469
  2. [DESIGN EXCELLENCE]: Use rich aesthetics. Default to modern UI frameworks if applicable. Never output plain, ugly HTML/CSS. Ensure responsive, premium feel with micro-animations.
2219
2470
  3. [CODE QUALITY]: Write clean, modular, SOLID code. Check for syntax errors carefully. Do not generate incomplete code blocks.
2220
2471
  4. [NO HALLUCINATION]: If you don't know, use tools (Grep/Read/Web) to find out. Do not guess file paths or APIs.
2221
2472
  5. [TOOL EXECUTION FIRST]: NEVER output full code blocks to the chat and tell the user to copy-paste. ALWAYS use the 'Write' or 'Edit' tool to apply changes directly to their files! The user cannot copy-paste code. You MUST do the work using tools.
2222
2473
 
2474
+ ## AGENT OPERATING MODE
2475
+ - Treat the repository, its memories, skills, rules, and bundled local resources as first-class context.
2476
+ - Before answering a task that may depend on project state, rules, skills, memories, local resources, or external facts, proactively call the relevant tool(s) to inspect them.
2477
+ - Prefer using the full tool set when needed: Read, Write, Edit, Bash, Glob, Grep, TaskCreate, TaskUpdate, TaskList, BrowserDebug, WebFetch, WebSearch.
2478
+ - If a question is ambiguous, inspect the project context first instead of guessing.
2479
+ - Winter's job is to amplify weaker models: decompose problems, pull the right context, and use tools to reach a stronger answer than raw inference alone would produce.
2480
+ - Always begin with a short plan before coding or tool execution, then refine the plan if new facts appear.
2481
+
2223
2482
  ## CRITICAL LANGUAGE RULE
2224
2483
  **You MUST always respond in Vietnamese (tiếng Việt).** Never respond in Chinese, Japanese, Korean or any other language unless the user explicitly asks you to. This is non-negotiable.
2225
2484
 
@@ -2260,8 +2519,8 @@ ${colors.reset}
2260
2519
  ## Project
2261
2520
  Working directory: ${this.projectPath}
2262
2521
  Current session: ${this.session.getSessionId().substring(0, 8)}
2263
- ${memoryStr}${plansStr}
2264
- ${context ? `\n## Project Context\n${context}` : ''}
2522
+ ${memoryStr}${plansStr}${skillsStr}${startupPlanStr}
2523
+ ${context ? `\n## Project Context\n${this.compactText(context, 6000, 'project context')}` : ''}
2265
2524
 
2266
2525
  Be helpful, be precise, and get things done. Always respond in Vietnamese.`;
2267
2526
  }
@@ -2269,12 +2528,17 @@ Be helpful, be precise, and get things done. Always respond in Vietnamese.`;
2269
2528
  getFastSystemPrompt() {
2270
2529
  const memories = this.session.getMemory();
2271
2530
  const memoryStr = memories.length > 0
2272
- ? `\nContext nhớ ngắn:\n${memories.slice(-8).map(m => `- ${m.text}`).join('\n')}`
2531
+ ? `\nContext nhớ ngắn:\n${this.summarizePromptList(memories.slice(-8), {
2532
+ limit: 8,
2533
+ maxEntryChars: 160,
2534
+ maxTotalChars: 1200,
2535
+ mapper: memory => memory.text,
2536
+ })}`
2273
2537
  : '';
2274
2538
 
2275
2539
  return `Bạn là Winter, trợ lý AI trả lời ngắn gọn bằng tiếng Việt.
2276
- Trả lời trực tiếp, không gọi tool, không tự bịa thông tin.
2277
- Nếu người dùng yêu cầu sửa file/chạy lệnh/đọc dự án thì nói ngắn rằng cần dùng chế độ tool.${memoryStr}`;
2540
+ Ưu tiên dùng tool context khi cần; không bịa thông tin.
2541
+ Nếu người dùng yêu cầu sửa file/chạy lệnh/đọc dự án thì hãy gọi tool tương ứng thay chỉ nói chung chung.${memoryStr}`;
2278
2542
  }
2279
2543
 
2280
2544
  // Tab completion
@@ -2296,9 +2560,26 @@ Nếu người dùng yêu cầu sửa file/chạy lệnh/đọc dự án thì n
2296
2560
  getAgentSystemPrompt(role, context = '') {
2297
2561
  const memories = this.session.getMemory();
2298
2562
  const plans = this.session.getPlans();
2299
-
2300
- let memoryStr = memories.length > 0 ? `\n## Memories (Important Context)\n${memories.map(m => `- ${m.text}`).join('\n')}` : '';
2301
- let plansStr = plans.length > 0 ? `\n## Active Plans & Tasks\n${plans.map(p => `- [${p.status}] ${p.title}: ${p.description}`).join('\n')}` : '';
2563
+ const sessionContext = this.session.getContext() || {};
2564
+
2565
+ let memoryStr = memories.length > 0 ? `\n## Memories (Important Context)\n${this.summarizePromptList(memories, {
2566
+ limit: 8,
2567
+ maxEntryChars: 220,
2568
+ maxTotalChars: 1600,
2569
+ mapper: memory => memory.text,
2570
+ })}` : '';
2571
+ let plansStr = plans.length > 0 ? `\n## Active Plans & Tasks\n${this.summarizePromptList(plans, {
2572
+ limit: 6,
2573
+ maxEntryChars: 260,
2574
+ maxTotalChars: 1600,
2575
+ mapper: plan => `[${plan.status}] ${plan.title}: ${plan.description}`,
2576
+ })}` : '';
2577
+ let skillsStr = Array.isArray(sessionContext.activeSkills) && sessionContext.activeSkills.length > 0
2578
+ ? `\n## Auto-applied Skills\n${sessionContext.activeSkills.slice(0, 12).map(skill => `- ${skill}`).join('\n')}${sessionContext.activeSkills.length > 12 ? '\n- ...' : ''}`
2579
+ : '';
2580
+ let startupPlanStr = sessionContext.bootstrapPlan?.title
2581
+ ? `\n## Startup Plan\n- ${sessionContext.bootstrapPlan.title}: ${sessionContext.bootstrapPlan.description}`
2582
+ : '';
2302
2583
 
2303
2584
  let rolePrompt = '';
2304
2585
  switch (role) {
@@ -2323,7 +2604,7 @@ Nếu người dùng yêu cầu sửa file/chạy lệnh/đọc dự án thì n
2323
2604
  }
2324
2605
 
2325
2606
  return `## CRITICAL AI RULES (MUST FOLLOW STRICTLY):
2326
- 1. [THINKING BEFORE CODING]: Always output your thought process briefly before generating code. Think about edge cases, design structure, and syntax correctness.
2607
+ 1. [THINKING BEFORE CODING]: State assumptions, constraints, and a brief plan before making changes. Be thorough enough to be useful, and do not invent facts.
2327
2608
  2. [DESIGN EXCELLENCE]: Use rich aesthetics. Default to modern UI frameworks if applicable. Never output plain, ugly HTML/CSS. Ensure responsive, premium feel with micro-animations.
2328
2609
  3. [CODE QUALITY]: Write clean, modular, SOLID code. Check for syntax errors carefully. Do not generate incomplete code blocks.
2329
2610
  4. [NO HALLUCINATION]: If you don't know, use tools (Grep/Read/Web) to find out. Do not guess file paths or APIs.
@@ -2333,15 +2614,53 @@ ${rolePrompt}
2333
2614
 
2334
2615
  ## Tool Rules
2335
2616
  - Canonical tools: Read, Write, Edit, Bash, Glob, Grep, TaskCreate, TaskUpdate, TaskList, BrowserDebug, WebFetch, WebSearch.
2617
+ - Treat skills, memories, bundled resources, local project rules, and the tool list as operational context. Use them proactively when relevant.
2336
2618
  - Current OS is ${process.platform === 'win32' ? 'Windows; Bash auto-detects PowerShell and cmd.exe syntax. Use shell="powershell" or shell="cmd" when needed.' : process.platform}.
2337
2619
  - Prefer Write/Edit for writing files. Bash accepts both PowerShell and cmd.exe on Windows, but do not use long echo chains for code files.
2338
2620
  - If a tool call fails because of an unknown alias, call the canonical tool name next.
2621
+ - Always start with a brief plan, then refine it when new facts appear.
2339
2622
 
2340
2623
  ## Project
2341
2624
  Working directory: ${this.projectPath}
2342
2625
  Current session: ${this.session.getSessionId().substring(0, 8)}
2343
- ${memoryStr}${plansStr}
2344
- ${context ? `\n## Project Context\n${context}` : ''}`;
2626
+ ${memoryStr}${plansStr}${skillsStr}${startupPlanStr}
2627
+ ${context ? `\n## Project Context\n${this.compactText(context, 6000, 'project context')}` : ''}`;
2628
+ }
2629
+
2630
+ compactText(text, maxChars = 1200, label = 'text') {
2631
+ const value = String(text ?? '');
2632
+ if (value.length <= maxChars) return value;
2633
+
2634
+ const headChars = Math.max(0, Math.floor(maxChars * 0.7));
2635
+ const tailChars = Math.max(0, Math.floor(maxChars * 0.2));
2636
+ const head = value.slice(0, headChars);
2637
+ const tail = tailChars > 0 ? value.slice(-tailChars) : '';
2638
+ const omitted = Math.max(0, value.length - head.length - tail.length);
2639
+
2640
+ return [
2641
+ head,
2642
+ `[${label} truncated: ${omitted} chars omitted]`,
2643
+ tail,
2644
+ ].filter(Boolean).join('\n');
2645
+ }
2646
+
2647
+ summarizePromptList(items, { limit = 8, maxEntryChars = 220, maxTotalChars = 1600, mapper = value => value?.text ?? String(value ?? '') } = {}) {
2648
+ const selected = [];
2649
+ let total = 0;
2650
+
2651
+ for (const item of items.slice(-limit)) {
2652
+ const raw = this.compactText(mapper(item), maxEntryChars, 'entry').trim();
2653
+ if (!raw) continue;
2654
+ if (total + raw.length > maxTotalChars && selected.length > 0) break;
2655
+ selected.push(`- ${raw}`);
2656
+ total += raw.length;
2657
+ }
2658
+
2659
+ if (items.length > selected.length) {
2660
+ selected.push(`- ... (${items.length - selected.length} mục đã được lược bớt)`);
2661
+ }
2662
+
2663
+ return selected.join('\n');
2345
2664
  }
2346
2665
 
2347
2666
  getAgentTools(role) {