winter-super-cli 2026.5.22 → 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.
- package/README.md +1 -1
- package/WINTER.md +6 -0
- package/bin/winter.js +77 -220
- package/package.json +1 -1
- package/resources/local/manifest.json +60 -57
- package/rules/default.md +1 -0
- package/src/ai/providers.js +38 -11
- package/src/cli/commands.js +24 -3
- package/src/cli/commands.test.js +116 -0
- package/src/cli/config.js +12 -0
- package/src/cli/repl.js +490 -141
- package/src/cli/repl.test.js +203 -2
- package/src/cli/snowflake-logo.js +15 -7
- package/src/cli/terminal-ui.js +125 -0
- package/src/cli/terminal-ui.test.js +33 -0
- package/src/plugins/manager.js +3 -1
- package/src/session/manager.js +44 -0
- package/src/session/manager.test.js +72 -0
- package/src/tools/executor.js +1 -1
- package/src/tools/executor.test.js +110 -0
- package/resources/local/claude/settings.json +0 -33
- package/resources/local/claude/todos/022bdc3c-e2c0-4a20-a74f-b348ed022c75-agent-022bdc3c-e2c0-4a20-a74f-b348ed022c75.json +0 -1
- package/resources/local/claude/todos/316f0e7d-5512-49fa-8c7f-edc75b777612-agent-316f0e7d-5512-49fa-8c7f-edc75b777612.json +0 -1
- package/resources/local/claude/todos/3676dc17-fca1-4692-934b-ce35e1965af6-agent-3676dc17-fca1-4692-934b-ce35e1965af6.json +0 -1
- package/resources/local/claude/todos/464493de-7f2a-45cf-93e8-ad73214afa10-agent-464493de-7f2a-45cf-93e8-ad73214afa10.json +0 -1
- package/resources/local/claude/todos/51f2e7a7-3f31-4692-a9b2-d3f3906aafea-agent-51f2e7a7-3f31-4692-a9b2-d3f3906aafea.json +0 -1
- package/resources/local/claude/todos/64a67dce-3d62-4a98-a548-b9c91a8e87e8-agent-64a67dce-3d62-4a98-a548-b9c91a8e87e8.json +0 -1
- package/resources/local/claude/todos/727a06e6-0ac2-41ca-8b81-2c14e4d40182-agent-727a06e6-0ac2-41ca-8b81-2c14e4d40182.json +0 -1
- package/resources/local/claude/todos/7d34d296-9b5a-4525-9b68-600d2ae20b59-agent-7d34d296-9b5a-4525-9b68-600d2ae20b59.json +0 -1
- package/resources/local/claude/todos/8c0606f1-5bcc-4176-8125-c5174fd69002-agent-8c0606f1-5bcc-4176-8125-c5174fd69002.json +0 -1
- package/resources/local/claude/todos/905aab16-5225-43f6-8ae4-c94491fd3a6f-agent-905aab16-5225-43f6-8ae4-c94491fd3a6f.json +0 -1
- package/resources/local/claude/todos/9dbe93f0-d62c-4c12-b4eb-0eecc437d625-agent-9dbe93f0-d62c-4c12-b4eb-0eecc437d625.json +0 -1
- package/resources/local/claude/todos/ad48500f-02a5-4f18-970b-82fb595d171f-agent-ad48500f-02a5-4f18-970b-82fb595d171f.json +0 -1
- package/resources/local/claude/todos/af86ea71-9907-4066-907c-68055e6c0081-agent-af86ea71-9907-4066-907c-68055e6c0081.json +0 -1
- package/resources/local/claude/todos/dbb0dc16-5d71-4f1d-a56c-db0741b3d485-agent-dbb0dc16-5d71-4f1d-a56c-db0741b3d485.json +0 -1
- package/resources/local/claude/todos/ff1ac487-eb0f-4c63-9360-fbb0a81bb5ae-agent-ff1ac487-eb0f-4c63-9360-fbb0a81bb5ae.json +0 -1
- package/resources/local/codex/config.toml +0 -84
- package/resources/local/codex/memories/MEMORY.md +0 -972
- package/resources/local/codex/memories/extensions/ad_hoc/instructions.md +0 -13
- package/resources/local/codex/memories/memory_summary.md +0 -188
- package/resources/local/codex/memories/raw_memories.md +0 -1488
- package/resources/local/codex/memories/rollout_summaries/2026-03-27T04-05-14-Iirb-nsis_full_installer_build_cpp_ocr_translator.md +0 -46
- package/resources/local/codex/memories/rollout_summaries/2026-03-28T06-18-17-Si3U-my_translator_overlay_lockfix_portable_nsis.md +0 -112
- package/resources/local/codex/memories/rollout_summaries/2026-04-15T06-42-11-2JMi-qelasy_timeout_and_watch_control_stability.md +0 -90
- package/resources/local/codex/memories/rollout_summaries/2026-04-16T03-12-59-z6Wi-request_all_row_click_detail_navigation.md +0 -42
- package/resources/local/codex/memories/rollout_summaries/2026-04-17T05-49-03-tNBk-my_translator_project_readability_audio_latency_clear_button.md +0 -75
- package/resources/local/codex/memories/rollout_summaries/2026-04-21T04-05-04-EXnh-nsis_packaging_harfbuzz_dll_qml_runtime_debug.md +0 -108
- package/resources/local/codex/memories/rollout_summaries/2026-04-22T03-48-40-VnNG-openclaw_opencode_sync_and_runtime_repair.md +0 -86
- package/resources/local/codex/memories/rollout_summaries/2026-04-22T06-49-49-R8yZ-web_book_user_portal_and_lint_fixes.md +0 -82
- package/resources/local/codex/memories/rollout_summaries/2026-04-22T06-50-35-ZaS1-smoke_admin_rbac_refund_connection_refused.md +0 -35
- package/resources/local/codex/memories/rollout_summaries/2026-04-22T11-05-04-aotT-nextjs_build_fix_statswidget_leaflet_ssr.md +0 -78
- package/resources/local/codex/memories/rollout_summaries/2026-04-23T03-22-24-a5q4-ui_still_looks_cloudflare_only.md +0 -41
- package/resources/local/codex/memories/rollout_summaries/2026-04-23T04-35-47-amlb-bayre247_hero_slide_above_search_form.md +0 -49
- package/resources/local/codex/memories/rollout_summaries/2026-04-23T04-59-21-lZWv-ocr_backend_parity_easyocr_tesseract_paddle_fallback.md +0 -92
- package/resources/local/codex/memories/rollout_summaries/2026-04-23T07-36-22-tPuo-request_workflow_editor_drag_edge_smaller_arrows_roadmap.md +0 -72
- package/resources/local/codex/memories/rollout_summaries/2026-04-24T08-01-05-Gb3B-checkin_shifts_workdays_assignments_and_checkout_overhaul.md +0 -90
- package/resources/local/codex/memories/rollout_summaries/2026-04-25T03-39-02-mbDr-web_book_refund_admin_popup_pagination_responsiveness.md +0 -151
- package/resources/local/codex/memories/rollout_summaries/2026-04-25T09-20-30-4usS-tool_scv_9router_custom_provider_and_paddle_ocr.md +0 -130
- package/resources/local/codex/memories/rollout_summaries/2026-05-06T10-19-38-mt2X-find_db_config_in_web_book_app_env.md +0 -40
- 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
- package/resources/local/codex/memories/rollout_summaries/2026-05-09T07-52-18-On1F-chakra_git_cleanup_readme_bilingual_publish_config.md +0 -88
- package/resources/local/codex/memories/rollout_summaries/2026-05-11T08-05-34-oMEl-check_crack_gui_logo_onefile_build.md +0 -68
- 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
|
|
88
|
-
const
|
|
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${
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
|
215
|
+
const resourcePaths = this.getResourcePaths();
|
|
216
|
+
const autoLoadTargets = [resourcePaths.agents, resourcePaths.designs, resourcePaths.karpathy];
|
|
180
217
|
|
|
181
|
-
for (const
|
|
218
|
+
for (const targetPath of autoLoadTargets) {
|
|
182
219
|
try {
|
|
183
|
-
const
|
|
184
|
-
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
|
|
192
|
-
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
253
|
+
if (!loaded) {
|
|
254
|
+
// nothing to load
|
|
255
|
+
}
|
|
256
|
+
}
|
|
197
257
|
} catch (e) {
|
|
198
|
-
//
|
|
258
|
+
// Ignore read errors for resources
|
|
199
259
|
}
|
|
200
260
|
}
|
|
201
261
|
|
|
202
|
-
//
|
|
203
|
-
const
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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.
|
|
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
|
|
1113
|
-
const
|
|
1114
|
-
const
|
|
1115
|
-
const
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
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
|
-
|
|
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
|
-
${
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
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 =
|
|
1362
|
-
const
|
|
1363
|
-
const
|
|
1364
|
-
const
|
|
1365
|
-
const
|
|
1366
|
-
const
|
|
1367
|
-
console.log(`\n${colors.magenta}
|
|
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,26 +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
|
|
1424
|
-
for (const line of
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
let remaining = line;
|
|
1430
|
-
let first = true;
|
|
1431
|
-
while (remaining.length > 0) {
|
|
1432
|
-
const chunk = remaining.substring(0, maxLen);
|
|
1433
|
-
remaining = remaining.substring(maxLen);
|
|
1434
|
-
const prefix = first ? statusIcon : ' ';
|
|
1435
|
-
console.log(`${colors.magenta}│${colors.reset} ${prefix} ${colors.dim}${chunk}${colors.reset}`);
|
|
1436
|
-
first = false;
|
|
1437
|
-
}
|
|
1438
|
-
}
|
|
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}`);
|
|
1439
1496
|
}
|
|
1440
1497
|
}
|
|
1441
1498
|
}
|
|
1442
|
-
console.log(`${colors.magenta}╰${'─'.repeat(
|
|
1499
|
+
console.log(`${colors.magenta}╰${'─'.repeat(innerWidth)}╯${colors.reset}\n`);
|
|
1443
1500
|
}
|
|
1444
1501
|
|
|
1445
1502
|
if (usedTools && !finalContent) {
|
|
@@ -1676,19 +1733,41 @@ ${colors.reset}
|
|
|
1676
1733
|
|
|
1677
1734
|
if (this.slashMenu.printedLines) {
|
|
1678
1735
|
readline.moveCursor(process.stdout, 0, -this.slashMenu.printedLines);
|
|
1679
|
-
readline.clearScreenDown(process.stdout);
|
|
1680
1736
|
}
|
|
1681
1737
|
|
|
1682
1738
|
process.stdout.write('\n');
|
|
1739
|
+
readline.clearLine(process.stdout, 1);
|
|
1683
1740
|
process.stdout.write(`${colors.dim}Commands${colors.reset}\n`);
|
|
1684
|
-
|
|
1741
|
+
|
|
1742
|
+
const maxDisplay = 5;
|
|
1743
|
+
const displayedMatches = matches.slice(0, maxDisplay);
|
|
1744
|
+
|
|
1745
|
+
displayedMatches.forEach((item, index) => {
|
|
1746
|
+
readline.clearLine(process.stdout, 1);
|
|
1685
1747
|
const usage = item.usage ? ` ${colors.dim}${item.usage}${colors.reset}` : '';
|
|
1686
1748
|
const pointer = index === this.slashMenu.selected ? `${colors.green}>${colors.reset}` : ' ';
|
|
1687
1749
|
process.stdout.write(`${pointer} ${colors.cyan}${item.cmd}${colors.reset} ${colors.dim}${item.desc}${colors.reset}${usage}\n`);
|
|
1688
1750
|
});
|
|
1751
|
+
|
|
1752
|
+
if (matches.length > maxDisplay) {
|
|
1753
|
+
readline.clearLine(process.stdout, 1);
|
|
1754
|
+
process.stdout.write(` ${colors.dim}... và ${matches.length - maxDisplay} lệnh khác (gõ tiếp để lọc)${colors.reset}\n`);
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
readline.clearLine(process.stdout, 1);
|
|
1689
1758
|
process.stdout.write(`${colors.dim}↑/↓ chọn · Enter/Tab dùng · Esc đóng${colors.reset}\n`);
|
|
1690
1759
|
|
|
1691
|
-
|
|
1760
|
+
// Xóa các dòng thừa nếu số lượng dòng mới ít hơn số lượng dòng cũ
|
|
1761
|
+
const currentLines = Math.min(matches.length, maxDisplay) + 3 + (matches.length > maxDisplay ? 1 : 0);
|
|
1762
|
+
if (this.slashMenu.printedLines > currentLines) {
|
|
1763
|
+
for (let i = 0; i < this.slashMenu.printedLines - currentLines; i++) {
|
|
1764
|
+
readline.clearLine(process.stdout, 1);
|
|
1765
|
+
process.stdout.write('\n');
|
|
1766
|
+
}
|
|
1767
|
+
readline.moveCursor(process.stdout, 0, -(this.slashMenu.printedLines - currentLines));
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
this.slashMenu.printedLines = currentLines;
|
|
1692
1771
|
this.rl.prompt(true);
|
|
1693
1772
|
}
|
|
1694
1773
|
|
|
@@ -2049,16 +2128,16 @@ ${colors.reset}
|
|
|
2049
2128
|
|
|
2050
2129
|
async chat(message, imageAttachments = []) {
|
|
2051
2130
|
try {
|
|
2052
|
-
const needsTools =
|
|
2053
|
-
const context =
|
|
2131
|
+
const needsTools = true;
|
|
2132
|
+
const context = await this.getProjectContext();
|
|
2054
2133
|
const messages = [
|
|
2055
|
-
{ role: 'system', content:
|
|
2134
|
+
{ role: 'system', content: this.getSystemPrompt(context) }
|
|
2056
2135
|
];
|
|
2057
2136
|
|
|
2058
2137
|
const history = this.getPromptHistory({
|
|
2059
|
-
limit:
|
|
2060
|
-
maxEntryChars:
|
|
2061
|
-
maxTotalChars:
|
|
2138
|
+
limit: 20,
|
|
2139
|
+
maxEntryChars: 2000,
|
|
2140
|
+
maxTotalChars: 12000,
|
|
2062
2141
|
});
|
|
2063
2142
|
for (const entry of history) {
|
|
2064
2143
|
messages.push({ role: entry.role, content: entry.content });
|
|
@@ -2079,7 +2158,7 @@ ${colors.reset}
|
|
|
2079
2158
|
messages.push({ role: 'user', content: message });
|
|
2080
2159
|
}
|
|
2081
2160
|
|
|
2082
|
-
const tools =
|
|
2161
|
+
const tools = this.getAgentTools('general');
|
|
2083
2162
|
const finalContent = await this.runConversation(messages, 'Thinking', tools);
|
|
2084
2163
|
|
|
2085
2164
|
await this.session.addToHistory({ role: 'user', content: message });
|
|
@@ -2091,12 +2170,7 @@ ${colors.reset}
|
|
|
2091
2170
|
}
|
|
2092
2171
|
|
|
2093
2172
|
shouldUseTools(message = '', imageAttachments = []) {
|
|
2094
|
-
|
|
2095
|
-
const text = String(message || '').toLowerCase();
|
|
2096
|
-
if (/[a-z]:[\\/]|\.([cm]?[jt]sx?|json|md|css|html|py|java|go|rs|php|rb|toml|ya?ml)\b/i.test(text)) {
|
|
2097
|
-
return true;
|
|
2098
|
-
}
|
|
2099
|
-
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;
|
|
2100
2174
|
}
|
|
2101
2175
|
|
|
2102
2176
|
getPromptHistory({ limit = 20, maxEntryChars = 2000, maxTotalChars = 12000 } = {}) {
|
|
@@ -2141,19 +2215,29 @@ ${colors.reset}
|
|
|
2141
2215
|
|
|
2142
2216
|
async getProjectContext() {
|
|
2143
2217
|
const context = [];
|
|
2144
|
-
const
|
|
2218
|
+
const projectInstructionFiles = await this.readProjectInstructionFiles();
|
|
2145
2219
|
|
|
2146
|
-
for (const file of
|
|
2220
|
+
for (const file of projectInstructionFiles) {
|
|
2147
2221
|
try {
|
|
2148
|
-
const
|
|
2149
|
-
|
|
2150
|
-
if (stat.isFile()) {
|
|
2151
|
-
const content = await fs.readFile(filePath, 'utf-8');
|
|
2152
|
-
context.push(`[${file}]\n${content.substring(0, 300)}...`);
|
|
2153
|
-
}
|
|
2222
|
+
const preview = this.compactText(file.content, 900, 'project instruction');
|
|
2223
|
+
context.push(`[${file.relativePath}]\n${preview}`);
|
|
2154
2224
|
} catch { }
|
|
2155
2225
|
}
|
|
2156
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
|
+
|
|
2157
2241
|
// Git Context
|
|
2158
2242
|
try {
|
|
2159
2243
|
const { execSync } = await import('child_process');
|
|
@@ -2161,35 +2245,240 @@ ${colors.reset}
|
|
|
2161
2245
|
if (gitStatus) {
|
|
2162
2246
|
context.push(`[Git Status]\n${gitStatus}`);
|
|
2163
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
|
+
|
|
2164
2253
|
// Get brief git diff for context
|
|
2165
|
-
const gitDiff = execSync('git diff', { cwd: this.projectPath, encoding: 'utf8', stdio: 'pipe', maxBuffer: 1024 * 50 }).trim().split('\n').slice(0,
|
|
2254
|
+
const gitDiff = execSync('git diff', { cwd: this.projectPath, encoding: 'utf8', stdio: 'pipe', maxBuffer: 1024 * 50 }).trim().split('\n').slice(0, 30).join('\n');
|
|
2166
2255
|
if (gitDiff) {
|
|
2167
|
-
context.push(`[Git Diff]\n${gitDiff}
|
|
2256
|
+
context.push(`[Git Diff]\n${this.compactText(gitDiff, 1800, 'git diff')}`);
|
|
2168
2257
|
}
|
|
2169
2258
|
}
|
|
2170
2259
|
} catch (e) {
|
|
2171
2260
|
// Not a git repo or git not installed
|
|
2172
2261
|
}
|
|
2173
2262
|
|
|
2174
|
-
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);
|
|
2175
2439
|
}
|
|
2176
2440
|
|
|
2177
2441
|
getSystemPrompt(context = '') {
|
|
2178
2442
|
const memories = this.session.getMemory();
|
|
2179
2443
|
const plans = this.session.getPlans();
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
let
|
|
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
|
+
: '';
|
|
2183
2464
|
|
|
2184
2465
|
return `You are Winter, an expert AI coding assistant.
|
|
2185
2466
|
|
|
2186
2467
|
## CRITICAL AI RULES (MUST FOLLOW STRICTLY):
|
|
2187
|
-
1. [THINKING BEFORE CODING]:
|
|
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.
|
|
2188
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.
|
|
2189
2470
|
3. [CODE QUALITY]: Write clean, modular, SOLID code. Check for syntax errors carefully. Do not generate incomplete code blocks.
|
|
2190
2471
|
4. [NO HALLUCINATION]: If you don't know, use tools (Grep/Read/Web) to find out. Do not guess file paths or APIs.
|
|
2191
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.
|
|
2192
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
|
+
|
|
2193
2482
|
## CRITICAL LANGUAGE RULE
|
|
2194
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.
|
|
2195
2484
|
|
|
@@ -2230,8 +2519,8 @@ ${colors.reset}
|
|
|
2230
2519
|
## Project
|
|
2231
2520
|
Working directory: ${this.projectPath}
|
|
2232
2521
|
Current session: ${this.session.getSessionId().substring(0, 8)}
|
|
2233
|
-
${memoryStr}${plansStr}
|
|
2234
|
-
${context ? `\n## Project Context\n${context}` : ''}
|
|
2522
|
+
${memoryStr}${plansStr}${skillsStr}${startupPlanStr}
|
|
2523
|
+
${context ? `\n## Project Context\n${this.compactText(context, 6000, 'project context')}` : ''}
|
|
2235
2524
|
|
|
2236
2525
|
Be helpful, be precise, and get things done. Always respond in Vietnamese.`;
|
|
2237
2526
|
}
|
|
@@ -2239,12 +2528,17 @@ Be helpful, be precise, and get things done. Always respond in Vietnamese.`;
|
|
|
2239
2528
|
getFastSystemPrompt() {
|
|
2240
2529
|
const memories = this.session.getMemory();
|
|
2241
2530
|
const memoryStr = memories.length > 0
|
|
2242
|
-
? `\nContext nhớ ngắn:\n${memories.slice(-8)
|
|
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
|
+
})}`
|
|
2243
2537
|
: '';
|
|
2244
2538
|
|
|
2245
2539
|
return `Bạn là Winter, trợ lý AI trả lời ngắn gọn bằng tiếng Việt.
|
|
2246
|
-
|
|
2247
|
-
Nếu người dùng yêu cầu sửa file/chạy lệnh/đọc dự án thì
|
|
2540
|
+
Ưu tiên dùng tool và 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 vì chỉ nói chung chung.${memoryStr}`;
|
|
2248
2542
|
}
|
|
2249
2543
|
|
|
2250
2544
|
// Tab completion
|
|
@@ -2266,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
|
|
|
2266
2560
|
getAgentSystemPrompt(role, context = '') {
|
|
2267
2561
|
const memories = this.session.getMemory();
|
|
2268
2562
|
const plans = this.session.getPlans();
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
let
|
|
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
|
+
: '';
|
|
2272
2583
|
|
|
2273
2584
|
let rolePrompt = '';
|
|
2274
2585
|
switch (role) {
|
|
@@ -2293,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
|
|
|
2293
2604
|
}
|
|
2294
2605
|
|
|
2295
2606
|
return `## CRITICAL AI RULES (MUST FOLLOW STRICTLY):
|
|
2296
|
-
1. [THINKING BEFORE CODING]:
|
|
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.
|
|
2297
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.
|
|
2298
2609
|
3. [CODE QUALITY]: Write clean, modular, SOLID code. Check for syntax errors carefully. Do not generate incomplete code blocks.
|
|
2299
2610
|
4. [NO HALLUCINATION]: If you don't know, use tools (Grep/Read/Web) to find out. Do not guess file paths or APIs.
|
|
@@ -2303,15 +2614,53 @@ ${rolePrompt}
|
|
|
2303
2614
|
|
|
2304
2615
|
## Tool Rules
|
|
2305
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.
|
|
2306
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}.
|
|
2307
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.
|
|
2308
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.
|
|
2309
2622
|
|
|
2310
2623
|
## Project
|
|
2311
2624
|
Working directory: ${this.projectPath}
|
|
2312
2625
|
Current session: ${this.session.getSessionId().substring(0, 8)}
|
|
2313
|
-
${memoryStr}${plansStr}
|
|
2314
|
-
${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');
|
|
2315
2664
|
}
|
|
2316
2665
|
|
|
2317
2666
|
getAgentTools(role) {
|