@yivan-lab/pretty-please 1.4.0 → 1.5.1
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 +32 -4
- package/bin/pls.tsx +153 -35
- package/dist/bin/pls.js +126 -23
- package/dist/package.json +10 -2
- package/dist/src/__integration__/command-generation.test.d.ts +5 -0
- package/dist/src/__integration__/command-generation.test.js +508 -0
- package/dist/src/__integration__/error-recovery.test.d.ts +5 -0
- package/dist/src/__integration__/error-recovery.test.js +511 -0
- package/dist/src/__integration__/shell-hook-workflow.test.d.ts +5 -0
- package/dist/src/__integration__/shell-hook-workflow.test.js +375 -0
- package/dist/src/__tests__/alias.test.d.ts +5 -0
- package/dist/src/__tests__/alias.test.js +421 -0
- package/dist/src/__tests__/chat-history.test.d.ts +5 -0
- package/dist/src/__tests__/chat-history.test.js +372 -0
- package/dist/src/__tests__/config.test.d.ts +5 -0
- package/dist/src/__tests__/config.test.js +822 -0
- package/dist/src/__tests__/history.test.d.ts +5 -0
- package/dist/src/__tests__/history.test.js +439 -0
- package/dist/src/__tests__/remote-history.test.d.ts +5 -0
- package/dist/src/__tests__/remote-history.test.js +641 -0
- package/dist/src/__tests__/remote.test.d.ts +5 -0
- package/dist/src/__tests__/remote.test.js +689 -0
- package/dist/src/__tests__/shell-hook-install.test.d.ts +5 -0
- package/dist/src/__tests__/shell-hook-install.test.js +413 -0
- package/dist/src/__tests__/shell-hook-remote.test.d.ts +5 -0
- package/dist/src/__tests__/shell-hook-remote.test.js +507 -0
- package/dist/src/__tests__/shell-hook.test.d.ts +5 -0
- package/dist/src/__tests__/shell-hook.test.js +440 -0
- package/dist/src/__tests__/sysinfo.test.d.ts +5 -0
- package/dist/src/__tests__/sysinfo.test.js +572 -0
- package/dist/src/__tests__/system-history.test.d.ts +5 -0
- package/dist/src/__tests__/system-history.test.js +457 -0
- package/dist/src/components/Chat.js +9 -28
- package/dist/src/config.d.ts +2 -0
- package/dist/src/config.js +30 -2
- package/dist/src/mastra-chat.js +6 -3
- package/dist/src/multi-step.js +6 -3
- package/dist/src/project-context.d.ts +22 -0
- package/dist/src/project-context.js +168 -0
- package/dist/src/prompts.d.ts +4 -4
- package/dist/src/prompts.js +23 -6
- package/dist/src/shell-hook.d.ts +13 -0
- package/dist/src/shell-hook.js +163 -33
- package/dist/src/sysinfo.d.ts +38 -9
- package/dist/src/sysinfo.js +245 -21
- package/dist/src/system-history.d.ts +5 -0
- package/dist/src/system-history.js +64 -18
- package/dist/src/ui/__tests__/theme.test.d.ts +5 -0
- package/dist/src/ui/__tests__/theme.test.js +688 -0
- package/dist/src/upgrade.js +3 -0
- package/dist/src/user-preferences.d.ts +44 -0
- package/dist/src/user-preferences.js +147 -0
- package/dist/src/utils/__tests__/platform-capabilities.test.d.ts +5 -0
- package/dist/src/utils/__tests__/platform-capabilities.test.js +214 -0
- package/dist/src/utils/__tests__/platform-exec.test.d.ts +5 -0
- package/dist/src/utils/__tests__/platform-exec.test.js +212 -0
- package/dist/src/utils/__tests__/platform-shell.test.d.ts +5 -0
- package/dist/src/utils/__tests__/platform-shell.test.js +300 -0
- package/dist/src/utils/__tests__/platform.test.d.ts +5 -0
- package/dist/src/utils/__tests__/platform.test.js +137 -0
- package/dist/src/utils/platform.d.ts +88 -0
- package/dist/src/utils/platform.js +331 -0
- package/package.json +10 -2
- package/src/__integration__/command-generation.test.ts +602 -0
- package/src/__integration__/error-recovery.test.ts +620 -0
- package/src/__integration__/shell-hook-workflow.test.ts +457 -0
- package/src/__tests__/alias.test.ts +545 -0
- package/src/__tests__/chat-history.test.ts +462 -0
- package/src/__tests__/config.test.ts +1043 -0
- package/src/__tests__/history.test.ts +538 -0
- package/src/__tests__/remote-history.test.ts +791 -0
- package/src/__tests__/remote.test.ts +866 -0
- package/src/__tests__/shell-hook-install.test.ts +510 -0
- package/src/__tests__/shell-hook-remote.test.ts +679 -0
- package/src/__tests__/shell-hook.test.ts +564 -0
- package/src/__tests__/sysinfo.test.ts +718 -0
- package/src/__tests__/system-history.test.ts +608 -0
- package/src/components/Chat.tsx +10 -37
- package/src/config.ts +29 -2
- package/src/mastra-chat.ts +8 -3
- package/src/multi-step.ts +7 -2
- package/src/project-context.ts +191 -0
- package/src/prompts.ts +26 -5
- package/src/shell-hook.ts +179 -33
- package/src/sysinfo.ts +326 -25
- package/src/system-history.ts +67 -14
- package/src/ui/__tests__/theme.test.ts +869 -0
- package/src/upgrade.ts +5 -0
- package/src/user-preferences.ts +178 -0
- package/src/utils/__tests__/platform-capabilities.test.ts +265 -0
- package/src/utils/__tests__/platform-exec.test.ts +278 -0
- package/src/utils/__tests__/platform-shell.test.ts +353 -0
- package/src/utils/__tests__/platform.test.ts +170 -0
- package/src/utils/platform.ts +431 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
// ================== 平台检测 ==================
|
|
5
|
+
/**
|
|
6
|
+
* 是否为 Windows 平台
|
|
7
|
+
*/
|
|
8
|
+
export function isWindows() {
|
|
9
|
+
return process.platform === 'win32';
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 是否为 macOS 平台
|
|
13
|
+
*/
|
|
14
|
+
export function isMacOS() {
|
|
15
|
+
return process.platform === 'darwin';
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 是否为 Linux 平台
|
|
19
|
+
*/
|
|
20
|
+
export function isLinux() {
|
|
21
|
+
return process.platform === 'linux';
|
|
22
|
+
}
|
|
23
|
+
// ================== Shell 检测 ==================
|
|
24
|
+
/**
|
|
25
|
+
* 检测 Windows Shell 类型
|
|
26
|
+
*/
|
|
27
|
+
function detectWindowsShell() {
|
|
28
|
+
// 1. 检查 PSModulePath 判断 PowerShell 版本
|
|
29
|
+
const psModulePath = process.env.PSModulePath || '';
|
|
30
|
+
if (psModulePath) {
|
|
31
|
+
// PowerShell 7+ 的 PSModulePath 包含 "PowerShell\7" 或 "PowerShell/7"
|
|
32
|
+
if (/PowerShell[\/\\]7/i.test(psModulePath)) {
|
|
33
|
+
return 'powershell7';
|
|
34
|
+
}
|
|
35
|
+
// Windows PowerShell 5.x 的 PSModulePath 包含 "WindowsPowerShell"
|
|
36
|
+
if (/WindowsPowerShell/i.test(psModulePath)) {
|
|
37
|
+
return 'powershell5';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// 2. 检查 PROMPT 环境变量(CMD 特有)
|
|
41
|
+
// 注意:在 PowerShell 中运行 CMD 时也可能有 PROMPT
|
|
42
|
+
if (process.env.PROMPT && !psModulePath) {
|
|
43
|
+
return 'cmd';
|
|
44
|
+
}
|
|
45
|
+
// 3. 尝试检测 pwsh 是否可用
|
|
46
|
+
if (commandExists('pwsh')) {
|
|
47
|
+
return 'powershell7';
|
|
48
|
+
}
|
|
49
|
+
// 4. 默认 PowerShell 5(Windows 内置)
|
|
50
|
+
return 'powershell5';
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 检测 Unix Shell 类型
|
|
54
|
+
*/
|
|
55
|
+
function detectUnixShell() {
|
|
56
|
+
const shell = process.env.SHELL || '';
|
|
57
|
+
if (shell.includes('zsh'))
|
|
58
|
+
return 'zsh';
|
|
59
|
+
if (shell.includes('bash'))
|
|
60
|
+
return 'bash';
|
|
61
|
+
if (shell.includes('fish'))
|
|
62
|
+
return 'fish';
|
|
63
|
+
return 'unknown';
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 检测当前 Shell 类型
|
|
67
|
+
*/
|
|
68
|
+
export function detectShell() {
|
|
69
|
+
if (isWindows()) {
|
|
70
|
+
return detectWindowsShell();
|
|
71
|
+
}
|
|
72
|
+
return detectUnixShell();
|
|
73
|
+
}
|
|
74
|
+
// ================== Shell 能力 ==================
|
|
75
|
+
/**
|
|
76
|
+
* 获取 Shell 配置文件路径
|
|
77
|
+
*/
|
|
78
|
+
function getShellConfigPath(shell) {
|
|
79
|
+
const home = os.homedir();
|
|
80
|
+
switch (shell) {
|
|
81
|
+
case 'zsh':
|
|
82
|
+
return path.join(home, '.zshrc');
|
|
83
|
+
case 'bash':
|
|
84
|
+
// macOS 使用 .bash_profile,Linux 使用 .bashrc
|
|
85
|
+
return isMacOS()
|
|
86
|
+
? path.join(home, '.bash_profile')
|
|
87
|
+
: path.join(home, '.bashrc');
|
|
88
|
+
case 'fish':
|
|
89
|
+
return path.join(home, '.config', 'fish', 'config.fish');
|
|
90
|
+
case 'powershell5':
|
|
91
|
+
return path.join(home, 'Documents', 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1');
|
|
92
|
+
case 'powershell7':
|
|
93
|
+
return path.join(home, 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1');
|
|
94
|
+
case 'cmd':
|
|
95
|
+
return null; // CMD 不支持配置文件
|
|
96
|
+
default:
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 获取 Shell 历史文件路径
|
|
102
|
+
*/
|
|
103
|
+
function getShellHistoryPath(shell) {
|
|
104
|
+
const home = os.homedir();
|
|
105
|
+
switch (shell) {
|
|
106
|
+
case 'zsh':
|
|
107
|
+
return process.env.HISTFILE || path.join(home, '.zsh_history');
|
|
108
|
+
case 'bash':
|
|
109
|
+
return process.env.HISTFILE || path.join(home, '.bash_history');
|
|
110
|
+
case 'fish':
|
|
111
|
+
return path.join(home, '.local', 'share', 'fish', 'fish_history');
|
|
112
|
+
case 'powershell5':
|
|
113
|
+
case 'powershell7':
|
|
114
|
+
// PowerShell 历史文件位置(PSReadLine)
|
|
115
|
+
const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
|
|
116
|
+
return path.join(appData, 'Microsoft', 'Windows', 'PowerShell', 'PSReadLine', 'ConsoleHost_history.txt');
|
|
117
|
+
case 'cmd':
|
|
118
|
+
return null; // CMD 不持久化历史
|
|
119
|
+
default:
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* 获取 Shell 可执行文件
|
|
125
|
+
*/
|
|
126
|
+
function getShellExecutable(shell) {
|
|
127
|
+
switch (shell) {
|
|
128
|
+
case 'zsh':
|
|
129
|
+
return process.env.SHELL || '/bin/zsh';
|
|
130
|
+
case 'bash':
|
|
131
|
+
return process.env.SHELL || '/bin/bash';
|
|
132
|
+
case 'fish':
|
|
133
|
+
return process.env.SHELL || '/usr/bin/fish';
|
|
134
|
+
case 'powershell5':
|
|
135
|
+
return 'powershell.exe';
|
|
136
|
+
case 'powershell7':
|
|
137
|
+
return 'pwsh.exe';
|
|
138
|
+
case 'cmd':
|
|
139
|
+
return process.env.COMSPEC || 'cmd.exe';
|
|
140
|
+
default:
|
|
141
|
+
return isWindows() ? 'powershell.exe' : '/bin/sh';
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* 获取 Shell 显示名称
|
|
146
|
+
*/
|
|
147
|
+
function getShellDisplayName(shell) {
|
|
148
|
+
switch (shell) {
|
|
149
|
+
case 'zsh': return 'Zsh';
|
|
150
|
+
case 'bash': return 'Bash';
|
|
151
|
+
case 'fish': return 'Fish';
|
|
152
|
+
case 'cmd': return 'CMD';
|
|
153
|
+
case 'powershell5': return 'PowerShell 5.x';
|
|
154
|
+
case 'powershell7': return 'PowerShell 7+';
|
|
155
|
+
default: return 'Unknown';
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* 获取 Shell 能力信息
|
|
160
|
+
*/
|
|
161
|
+
export function getShellCapabilities(shell) {
|
|
162
|
+
return {
|
|
163
|
+
supportsHook: shell !== 'cmd' && shell !== 'unknown',
|
|
164
|
+
supportsHistory: shell !== 'cmd' && shell !== 'unknown',
|
|
165
|
+
configPath: getShellConfigPath(shell),
|
|
166
|
+
historyPath: getShellHistoryPath(shell),
|
|
167
|
+
executable: getShellExecutable(shell),
|
|
168
|
+
displayName: getShellDisplayName(shell),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
// ================== 命令检测 ==================
|
|
172
|
+
/**
|
|
173
|
+
* 检测命令是否存在(跨平台)
|
|
174
|
+
*/
|
|
175
|
+
export function commandExists(command) {
|
|
176
|
+
try {
|
|
177
|
+
if (isWindows()) {
|
|
178
|
+
// Windows: 使用 where 命令
|
|
179
|
+
execSync(`where ${command}`, { stdio: 'ignore' });
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
// Unix: 使用 command -v(比 which 更可靠)
|
|
183
|
+
execSync(`command -v ${command}`, { stdio: 'ignore', shell: '/bin/sh' });
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* 批量检测命令是否存在(优化性能)
|
|
193
|
+
* @returns 返回存在的命令列表
|
|
194
|
+
*/
|
|
195
|
+
export function batchCommandExists(commands) {
|
|
196
|
+
if (commands.length === 0)
|
|
197
|
+
return [];
|
|
198
|
+
const available = [];
|
|
199
|
+
if (isWindows()) {
|
|
200
|
+
// Windows: 使用 PowerShell 批量检测
|
|
201
|
+
const batchSize = 20;
|
|
202
|
+
for (let i = 0; i < commands.length; i += batchSize) {
|
|
203
|
+
const batch = commands.slice(i, i + batchSize);
|
|
204
|
+
try {
|
|
205
|
+
// 使用 Get-Command 批量检测
|
|
206
|
+
const script = batch
|
|
207
|
+
.map(cmd => `if(Get-Command ${cmd} -ErrorAction SilentlyContinue){Write-Output ${cmd}}`)
|
|
208
|
+
.join(';');
|
|
209
|
+
const result = execSync(`powershell -NoProfile -Command "${script}"`, {
|
|
210
|
+
encoding: 'utf-8',
|
|
211
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
212
|
+
timeout: 5000,
|
|
213
|
+
});
|
|
214
|
+
available.push(...result.trim().split(/\r?\n/).filter(Boolean));
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// 这批失败,逐个检测
|
|
218
|
+
for (const cmd of batch) {
|
|
219
|
+
if (commandExists(cmd)) {
|
|
220
|
+
available.push(cmd);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
// Unix: 使用 shell 批量检测
|
|
228
|
+
const batchSize = 20;
|
|
229
|
+
for (let i = 0; i < commands.length; i += batchSize) {
|
|
230
|
+
const batch = commands.slice(i, i + batchSize);
|
|
231
|
+
const script = `(${batch
|
|
232
|
+
.map(cmd => `command -v ${cmd} >/dev/null 2>&1 && echo ${cmd}`)
|
|
233
|
+
.join('; ')}) 2>/dev/null || true`;
|
|
234
|
+
try {
|
|
235
|
+
const result = execSync(script, {
|
|
236
|
+
encoding: 'utf-8',
|
|
237
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
238
|
+
timeout: 500,
|
|
239
|
+
});
|
|
240
|
+
available.push(...result.trim().split('\n').filter(Boolean));
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// 这批失败,跳过
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return available;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* 构建命令执行配置
|
|
251
|
+
* 处理不同 Shell 的语法差异
|
|
252
|
+
*/
|
|
253
|
+
export function buildShellExecConfig(command, shell) {
|
|
254
|
+
const currentShell = shell || detectShell();
|
|
255
|
+
const executable = getShellExecutable(currentShell);
|
|
256
|
+
switch (currentShell) {
|
|
257
|
+
case 'bash':
|
|
258
|
+
return {
|
|
259
|
+
shell: executable,
|
|
260
|
+
args: ['-c', `set -o pipefail; ${command}`],
|
|
261
|
+
command: `set -o pipefail; ${command}`,
|
|
262
|
+
};
|
|
263
|
+
case 'zsh':
|
|
264
|
+
return {
|
|
265
|
+
shell: executable,
|
|
266
|
+
args: ['-c', `setopt pipefail; ${command}`],
|
|
267
|
+
command: `setopt pipefail; ${command}`,
|
|
268
|
+
};
|
|
269
|
+
case 'fish':
|
|
270
|
+
// Fish 不需要特殊处理 pipefail
|
|
271
|
+
return {
|
|
272
|
+
shell: executable,
|
|
273
|
+
args: ['-c', command],
|
|
274
|
+
command,
|
|
275
|
+
};
|
|
276
|
+
case 'powershell5':
|
|
277
|
+
case 'powershell7':
|
|
278
|
+
// PowerShell: 使用 $ErrorActionPreference 处理错误
|
|
279
|
+
// -NoProfile 加快启动速度
|
|
280
|
+
// -Command 执行命令
|
|
281
|
+
return {
|
|
282
|
+
shell: executable,
|
|
283
|
+
args: ['-NoProfile', '-Command', command],
|
|
284
|
+
command,
|
|
285
|
+
};
|
|
286
|
+
case 'cmd':
|
|
287
|
+
return {
|
|
288
|
+
shell: executable,
|
|
289
|
+
args: ['/c', command],
|
|
290
|
+
command,
|
|
291
|
+
};
|
|
292
|
+
default:
|
|
293
|
+
// 默认使用 sh
|
|
294
|
+
return {
|
|
295
|
+
shell: isWindows() ? 'powershell.exe' : '/bin/sh',
|
|
296
|
+
args: [isWindows() ? '-Command' : '-c', command],
|
|
297
|
+
command,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* 获取默认 Shell(用于交互式执行)
|
|
303
|
+
*/
|
|
304
|
+
export function getDefaultShell() {
|
|
305
|
+
if (isWindows()) {
|
|
306
|
+
const shell = detectWindowsShell();
|
|
307
|
+
return getShellExecutable(shell);
|
|
308
|
+
}
|
|
309
|
+
return process.env.SHELL || '/bin/bash';
|
|
310
|
+
}
|
|
311
|
+
// ================== 路径处理 ==================
|
|
312
|
+
/**
|
|
313
|
+
* 获取 pls 配置目录
|
|
314
|
+
* 统一使用 ~/.please
|
|
315
|
+
*/
|
|
316
|
+
export function getConfigDir() {
|
|
317
|
+
return path.join(os.homedir(), '.please');
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* 将路径转换为当前平台格式
|
|
321
|
+
*/
|
|
322
|
+
export function normalizePath(p) {
|
|
323
|
+
return path.normalize(p);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* 获取用于 PowerShell 脚本中的路径
|
|
327
|
+
* 使用 $env:USERPROFILE 而不是硬编码路径
|
|
328
|
+
*/
|
|
329
|
+
export function getPowerShellConfigDir() {
|
|
330
|
+
return '$env:USERPROFILE\\.please';
|
|
331
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yivan-lab/pretty-please",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "AI 驱动的命令行工具,将自然语言转换为可执行的 Shell 命令",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
"dev": "tsx bin/pls.tsx",
|
|
12
12
|
"build": "tsc && node scripts/postbuild.js",
|
|
13
13
|
"start": "node dist/bin/pls.js",
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:watch": "vitest",
|
|
16
|
+
"test:ui": "vitest --ui",
|
|
17
|
+
"test:coverage": "vitest run --coverage",
|
|
14
18
|
"link:dev": "mkdir -p ~/.local/bin && ln -sf \"$(pwd)/bin/pls.tsx\" ~/.local/bin/pls-dev && echo '✅ pls-dev 已链接到 ~/.local/bin/pls-dev'",
|
|
15
19
|
"unlink:dev": "rm -f ~/.local/bin/pls-dev && echo '✅ pls-dev 已移除'",
|
|
16
20
|
"prepublishOnly": "pnpm build"
|
|
@@ -55,6 +59,7 @@
|
|
|
55
59
|
"chalk": "^5.6.2",
|
|
56
60
|
"cli-highlight": "^2.1.11",
|
|
57
61
|
"commander": "^14.0.2",
|
|
62
|
+
"detect-package-manager": "^3.0.2",
|
|
58
63
|
"ink": "^6.5.1",
|
|
59
64
|
"ink-box": "^2.0.0",
|
|
60
65
|
"ink-markdown": "^1.0.4",
|
|
@@ -73,8 +78,11 @@
|
|
|
73
78
|
"@types/hast": "^3.0.4",
|
|
74
79
|
"@types/node": "^25.0.2",
|
|
75
80
|
"@types/react": "^19.2.7",
|
|
81
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
82
|
+
"@vitest/ui": "^4.0.16",
|
|
76
83
|
"react-devtools-core": "^7.0.1",
|
|
77
84
|
"tsx": "^4.21.0",
|
|
78
|
-
"typescript": "^5.9.3"
|
|
85
|
+
"typescript": "^5.9.3",
|
|
86
|
+
"vitest": "^4.0.16"
|
|
79
87
|
}
|
|
80
88
|
}
|