@yivan-lab/pretty-please 1.4.0 → 1.5.0
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 +30 -2
- 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,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell 检测专项测试
|
|
3
|
+
* 测试各种平台和环境下的 Shell 类型检测
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
6
|
+
import { detectShell } from '../platform';
|
|
7
|
+
import { execSync } from 'child_process';
|
|
8
|
+
// Mock child_process 用于控制 commandExists 的行为
|
|
9
|
+
vi.mock('child_process', () => ({
|
|
10
|
+
execSync: vi.fn(),
|
|
11
|
+
exec: vi.fn(),
|
|
12
|
+
spawn: vi.fn(),
|
|
13
|
+
}));
|
|
14
|
+
const mockExecSync = vi.mocked(execSync);
|
|
15
|
+
describe('Shell Detection - Windows', () => {
|
|
16
|
+
// 保存原始环境
|
|
17
|
+
const originalPlatform = process.platform;
|
|
18
|
+
const originalEnv = { ...process.env };
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
vi.clearAllMocks();
|
|
21
|
+
// 默认让 commandExists 返回 false(execSync 抛出错误)
|
|
22
|
+
mockExecSync.mockImplementation(() => {
|
|
23
|
+
throw new Error('Command not found');
|
|
24
|
+
});
|
|
25
|
+
// 清空环境变量
|
|
26
|
+
vi.stubGlobal('process', {
|
|
27
|
+
...process,
|
|
28
|
+
platform: 'win32',
|
|
29
|
+
env: {},
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
// 恢复原始环境
|
|
34
|
+
vi.unstubAllGlobals();
|
|
35
|
+
Object.defineProperty(process, 'platform', {
|
|
36
|
+
value: originalPlatform,
|
|
37
|
+
});
|
|
38
|
+
process.env = { ...originalEnv };
|
|
39
|
+
});
|
|
40
|
+
it('应该检测到 PowerShell 7+ (路径包含 PowerShell\\7)', () => {
|
|
41
|
+
vi.stubGlobal('process', {
|
|
42
|
+
...process,
|
|
43
|
+
platform: 'win32',
|
|
44
|
+
env: {
|
|
45
|
+
PSModulePath: 'C:\\Program Files\\PowerShell\\7\\Modules',
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
const result = detectShell();
|
|
49
|
+
expect(result).toBe('powershell7');
|
|
50
|
+
});
|
|
51
|
+
it('应该检测到 PowerShell 7+ (路径包含 PowerShell/7)', () => {
|
|
52
|
+
vi.stubGlobal('process', {
|
|
53
|
+
...process,
|
|
54
|
+
platform: 'win32',
|
|
55
|
+
env: {
|
|
56
|
+
PSModulePath: 'C:/Program Files/PowerShell/7/Modules',
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
const result = detectShell();
|
|
60
|
+
expect(result).toBe('powershell7');
|
|
61
|
+
});
|
|
62
|
+
it('应该检测到 PowerShell 7+ (大小写不敏感)', () => {
|
|
63
|
+
vi.stubGlobal('process', {
|
|
64
|
+
...process,
|
|
65
|
+
platform: 'win32',
|
|
66
|
+
env: {
|
|
67
|
+
PSModulePath: 'C:\\PROGRAM FILES\\powershell\\7\\modules',
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
const result = detectShell();
|
|
71
|
+
expect(result).toBe('powershell7');
|
|
72
|
+
});
|
|
73
|
+
it('应该检测到 PowerShell 5.x (路径包含 WindowsPowerShell)', () => {
|
|
74
|
+
vi.stubGlobal('process', {
|
|
75
|
+
...process,
|
|
76
|
+
platform: 'win32',
|
|
77
|
+
env: {
|
|
78
|
+
PSModulePath: 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\Modules',
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
const result = detectShell();
|
|
82
|
+
expect(result).toBe('powershell5');
|
|
83
|
+
});
|
|
84
|
+
it('应该检测到 PowerShell 5.x (大小写不敏感)', () => {
|
|
85
|
+
vi.stubGlobal('process', {
|
|
86
|
+
...process,
|
|
87
|
+
platform: 'win32',
|
|
88
|
+
env: {
|
|
89
|
+
PSModulePath: 'C:\\Windows\\System32\\WINDOWSPOWERSHELL\\v1.0\\Modules',
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
const result = detectShell();
|
|
93
|
+
expect(result).toBe('powershell5');
|
|
94
|
+
});
|
|
95
|
+
it('应该检测到 CMD (PROMPT 存在且无 PSModulePath)', () => {
|
|
96
|
+
vi.stubGlobal('process', {
|
|
97
|
+
...process,
|
|
98
|
+
platform: 'win32',
|
|
99
|
+
env: {
|
|
100
|
+
PROMPT: '$P$G',
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
const result = detectShell();
|
|
104
|
+
expect(result).toBe('cmd');
|
|
105
|
+
});
|
|
106
|
+
it('应该忽略 PROMPT 如果 PSModulePath 存在 (避免 PowerShell 中运行 CMD 的误判)', () => {
|
|
107
|
+
vi.stubGlobal('process', {
|
|
108
|
+
...process,
|
|
109
|
+
platform: 'win32',
|
|
110
|
+
env: {
|
|
111
|
+
PROMPT: '$P$G',
|
|
112
|
+
PSModulePath: 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\Modules',
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
const result = detectShell();
|
|
116
|
+
expect(result).toBe('powershell5'); // 应该是 PowerShell,不是 CMD
|
|
117
|
+
});
|
|
118
|
+
it('应该默认降级到 PowerShell 5 (Windows 环境无明确特征)', () => {
|
|
119
|
+
vi.stubGlobal('process', {
|
|
120
|
+
...process,
|
|
121
|
+
platform: 'win32',
|
|
122
|
+
env: {},
|
|
123
|
+
});
|
|
124
|
+
const result = detectShell();
|
|
125
|
+
expect(result).toBe('powershell5');
|
|
126
|
+
});
|
|
127
|
+
it('应该处理空的 PSModulePath', () => {
|
|
128
|
+
vi.stubGlobal('process', {
|
|
129
|
+
...process,
|
|
130
|
+
platform: 'win32',
|
|
131
|
+
env: {
|
|
132
|
+
PSModulePath: '',
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
const result = detectShell();
|
|
136
|
+
expect(result).toBe('powershell5');
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
describe('Shell Detection - Unix', () => {
|
|
140
|
+
const originalPlatform = process.platform;
|
|
141
|
+
const originalEnv = { ...process.env };
|
|
142
|
+
beforeEach(() => {
|
|
143
|
+
vi.stubGlobal('process', {
|
|
144
|
+
...process,
|
|
145
|
+
platform: 'darwin',
|
|
146
|
+
env: {},
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
afterEach(() => {
|
|
150
|
+
vi.unstubAllGlobals();
|
|
151
|
+
Object.defineProperty(process, 'platform', {
|
|
152
|
+
value: originalPlatform,
|
|
153
|
+
});
|
|
154
|
+
process.env = { ...originalEnv };
|
|
155
|
+
});
|
|
156
|
+
it('应该检测到 Zsh', () => {
|
|
157
|
+
vi.stubGlobal('process', {
|
|
158
|
+
...process,
|
|
159
|
+
platform: 'darwin',
|
|
160
|
+
env: {
|
|
161
|
+
SHELL: '/bin/zsh',
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
const result = detectShell();
|
|
165
|
+
expect(result).toBe('zsh');
|
|
166
|
+
});
|
|
167
|
+
it('应该检测到 Bash', () => {
|
|
168
|
+
vi.stubGlobal('process', {
|
|
169
|
+
...process,
|
|
170
|
+
platform: 'linux',
|
|
171
|
+
env: {
|
|
172
|
+
SHELL: '/bin/bash',
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
const result = detectShell();
|
|
176
|
+
expect(result).toBe('bash');
|
|
177
|
+
});
|
|
178
|
+
it('应该检测到 Fish', () => {
|
|
179
|
+
vi.stubGlobal('process', {
|
|
180
|
+
...process,
|
|
181
|
+
platform: 'linux',
|
|
182
|
+
env: {
|
|
183
|
+
SHELL: '/usr/bin/fish',
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
const result = detectShell();
|
|
187
|
+
expect(result).toBe('fish');
|
|
188
|
+
});
|
|
189
|
+
it('应该处理 SHELL 路径的多种格式 (Zsh)', () => {
|
|
190
|
+
const testCases = [
|
|
191
|
+
'/usr/local/bin/zsh',
|
|
192
|
+
'/opt/homebrew/bin/zsh',
|
|
193
|
+
'zsh',
|
|
194
|
+
];
|
|
195
|
+
for (const shellPath of testCases) {
|
|
196
|
+
vi.stubGlobal('process', {
|
|
197
|
+
...process,
|
|
198
|
+
platform: 'darwin',
|
|
199
|
+
env: {
|
|
200
|
+
SHELL: shellPath,
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
const result = detectShell();
|
|
204
|
+
expect(result).toBe('zsh');
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
it('应该处理 SHELL 路径的多种格式 (Bash)', () => {
|
|
208
|
+
const testCases = [
|
|
209
|
+
'/usr/local/bin/bash',
|
|
210
|
+
'/opt/homebrew/bin/bash',
|
|
211
|
+
'bash',
|
|
212
|
+
];
|
|
213
|
+
for (const shellPath of testCases) {
|
|
214
|
+
vi.stubGlobal('process', {
|
|
215
|
+
...process,
|
|
216
|
+
platform: 'linux',
|
|
217
|
+
env: {
|
|
218
|
+
SHELL: shellPath,
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
const result = detectShell();
|
|
222
|
+
expect(result).toBe('bash');
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
it('应该降级到 unknown (SHELL 为空)', () => {
|
|
226
|
+
vi.stubGlobal('process', {
|
|
227
|
+
...process,
|
|
228
|
+
platform: 'linux',
|
|
229
|
+
env: {
|
|
230
|
+
SHELL: '',
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
const result = detectShell();
|
|
234
|
+
expect(result).toBe('unknown');
|
|
235
|
+
});
|
|
236
|
+
it('应该降级到 unknown (SHELL 不匹配)', () => {
|
|
237
|
+
vi.stubGlobal('process', {
|
|
238
|
+
...process,
|
|
239
|
+
platform: 'linux',
|
|
240
|
+
env: {
|
|
241
|
+
SHELL: '/usr/bin/tcsh',
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
const result = detectShell();
|
|
245
|
+
expect(result).toBe('unknown');
|
|
246
|
+
});
|
|
247
|
+
it('应该降级到 unknown (无 SHELL 环境变量)', () => {
|
|
248
|
+
vi.stubGlobal('process', {
|
|
249
|
+
...process,
|
|
250
|
+
platform: 'linux',
|
|
251
|
+
env: {},
|
|
252
|
+
});
|
|
253
|
+
const result = detectShell();
|
|
254
|
+
expect(result).toBe('unknown');
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
describe('Shell Detection - Edge Cases', () => {
|
|
258
|
+
const originalPlatform = process.platform;
|
|
259
|
+
const originalEnv = { ...process.env };
|
|
260
|
+
afterEach(() => {
|
|
261
|
+
vi.unstubAllGlobals();
|
|
262
|
+
Object.defineProperty(process, 'platform', {
|
|
263
|
+
value: originalPlatform,
|
|
264
|
+
});
|
|
265
|
+
process.env = { ...originalEnv };
|
|
266
|
+
});
|
|
267
|
+
it('应该正确处理 macOS 平台', () => {
|
|
268
|
+
vi.stubGlobal('process', {
|
|
269
|
+
...process,
|
|
270
|
+
platform: 'darwin',
|
|
271
|
+
env: {
|
|
272
|
+
SHELL: '/bin/zsh',
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
const result = detectShell();
|
|
276
|
+
expect(result).toBe('zsh');
|
|
277
|
+
});
|
|
278
|
+
it('应该正确处理 Linux 平台', () => {
|
|
279
|
+
vi.stubGlobal('process', {
|
|
280
|
+
...process,
|
|
281
|
+
platform: 'linux',
|
|
282
|
+
env: {
|
|
283
|
+
SHELL: '/bin/bash',
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
const result = detectShell();
|
|
287
|
+
expect(result).toBe('bash');
|
|
288
|
+
});
|
|
289
|
+
it('应该正确处理 Windows 平台', () => {
|
|
290
|
+
vi.stubGlobal('process', {
|
|
291
|
+
...process,
|
|
292
|
+
platform: 'win32',
|
|
293
|
+
env: {
|
|
294
|
+
PSModulePath: 'C:\\Program Files\\PowerShell\\7\\Modules',
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
const result = detectShell();
|
|
298
|
+
expect(result).toBe('powershell7');
|
|
299
|
+
});
|
|
300
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 平台检测和通用函数测试
|
|
3
|
+
* 测试基础的平台判断函数和其他通用工具
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
6
|
+
import { isWindows, isMacOS, isLinux, getDefaultShell, getConfigDir, getPowerShellConfigDir } from '../platform';
|
|
7
|
+
describe('Platform Detection', () => {
|
|
8
|
+
const originalPlatform = process.platform;
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
Object.defineProperty(process, 'platform', {
|
|
11
|
+
value: originalPlatform,
|
|
12
|
+
writable: true,
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
it('isWindows() 在 Windows 平台应该返回 true', () => {
|
|
16
|
+
Object.defineProperty(process, 'platform', { value: 'win32', writable: true });
|
|
17
|
+
expect(isWindows()).toBe(true);
|
|
18
|
+
expect(isMacOS()).toBe(false);
|
|
19
|
+
expect(isLinux()).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
it('isMacOS() 在 macOS 平台应该返回 true', () => {
|
|
22
|
+
Object.defineProperty(process, 'platform', { value: 'darwin', writable: true });
|
|
23
|
+
expect(isWindows()).toBe(false);
|
|
24
|
+
expect(isMacOS()).toBe(true);
|
|
25
|
+
expect(isLinux()).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
it('isLinux() 在 Linux 平台应该返回 true', () => {
|
|
28
|
+
Object.defineProperty(process, 'platform', { value: 'linux', writable: true });
|
|
29
|
+
expect(isWindows()).toBe(false);
|
|
30
|
+
expect(isMacOS()).toBe(false);
|
|
31
|
+
expect(isLinux()).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
it('应该只有一个平台为 true', () => {
|
|
34
|
+
// 无论在哪个平台,三个函数应该只有一个返回 true
|
|
35
|
+
const results = [isWindows(), isMacOS(), isLinux()];
|
|
36
|
+
const trueCount = results.filter(r => r === true).length;
|
|
37
|
+
expect(trueCount).toBe(1);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe('getDefaultShell', () => {
|
|
41
|
+
const originalPlatform = process.platform;
|
|
42
|
+
const originalEnv = { ...process.env };
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
Object.defineProperty(process, 'platform', {
|
|
45
|
+
value: originalPlatform,
|
|
46
|
+
});
|
|
47
|
+
process.env = { ...originalEnv };
|
|
48
|
+
});
|
|
49
|
+
it('Unix 平台应该返回 $SHELL', () => {
|
|
50
|
+
Object.defineProperty(process, 'platform', { value: 'linux', writable: true });
|
|
51
|
+
process.env.SHELL = '/bin/zsh';
|
|
52
|
+
const shell = getDefaultShell();
|
|
53
|
+
expect(shell).toBe('/bin/zsh');
|
|
54
|
+
});
|
|
55
|
+
it('Unix 平台无 $SHELL 应该降级到 /bin/bash', () => {
|
|
56
|
+
Object.defineProperty(process, 'platform', { value: 'linux', writable: true });
|
|
57
|
+
delete process.env.SHELL;
|
|
58
|
+
const shell = getDefaultShell();
|
|
59
|
+
expect(shell).toBe('/bin/bash');
|
|
60
|
+
});
|
|
61
|
+
it('Windows 平台 PowerShell 7 应该返回 pwsh.exe', () => {
|
|
62
|
+
Object.defineProperty(process, 'platform', { value: 'win32', writable: true });
|
|
63
|
+
process.env.PSModulePath = 'C:\\Program Files\\PowerShell\\7\\Modules';
|
|
64
|
+
const shell = getDefaultShell();
|
|
65
|
+
expect(shell).toBe('pwsh.exe');
|
|
66
|
+
});
|
|
67
|
+
it('Windows 平台 PowerShell 5 应该返回 powershell.exe', () => {
|
|
68
|
+
Object.defineProperty(process, 'platform', { value: 'win32', writable: true });
|
|
69
|
+
process.env.PSModulePath = 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\Modules';
|
|
70
|
+
delete process.env.PROMPT;
|
|
71
|
+
const shell = getDefaultShell();
|
|
72
|
+
expect(shell).toBe('powershell.exe');
|
|
73
|
+
});
|
|
74
|
+
it('Windows 平台 CMD 应该返回 cmd.exe 或 $COMSPEC', () => {
|
|
75
|
+
Object.defineProperty(process, 'platform', { value: 'win32', writable: true });
|
|
76
|
+
process.env.PROMPT = '$P$G';
|
|
77
|
+
delete process.env.PSModulePath;
|
|
78
|
+
delete process.env.COMSPEC;
|
|
79
|
+
const shell = getDefaultShell();
|
|
80
|
+
expect(shell).toBe('cmd.exe');
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe('getConfigDir', () => {
|
|
84
|
+
it('应该返回 ~/.please 目录', () => {
|
|
85
|
+
const configDir = getConfigDir();
|
|
86
|
+
expect(configDir).toContain('.please');
|
|
87
|
+
expect(configDir).toMatch(/[\/\\]\.please$/);
|
|
88
|
+
});
|
|
89
|
+
it('应该使用用户 home 目录', () => {
|
|
90
|
+
const os = require('os');
|
|
91
|
+
const home = os.homedir();
|
|
92
|
+
const configDir = getConfigDir();
|
|
93
|
+
expect(configDir).toContain(home);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('getPowerShellConfigDir', () => {
|
|
97
|
+
it('应该返回 PowerShell 变量格式', () => {
|
|
98
|
+
const psDir = getPowerShellConfigDir();
|
|
99
|
+
expect(psDir).toBe('$env:USERPROFILE\\.please');
|
|
100
|
+
});
|
|
101
|
+
it('返回值应该是 PowerShell 可识别的路径', () => {
|
|
102
|
+
const psDir = getPowerShellConfigDir();
|
|
103
|
+
// PowerShell 路径应该:
|
|
104
|
+
// 1. 使用 $env:USERPROFILE 而不是硬编码路径
|
|
105
|
+
// 2. 使用反斜杠(Windows 风格)
|
|
106
|
+
expect(psDir).toMatch(/^\$env:USERPROFILE/);
|
|
107
|
+
expect(psDir).toContain('\\');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
describe('Integration - Platform 和 Shell 检测配合', () => {
|
|
111
|
+
const originalPlatform = process.platform;
|
|
112
|
+
const originalEnv = { ...process.env };
|
|
113
|
+
afterEach(() => {
|
|
114
|
+
Object.defineProperty(process, 'platform', {
|
|
115
|
+
value: originalPlatform,
|
|
116
|
+
});
|
|
117
|
+
process.env = { ...originalEnv };
|
|
118
|
+
});
|
|
119
|
+
it('macOS + Zsh 应该正常工作', () => {
|
|
120
|
+
Object.defineProperty(process, 'platform', { value: 'darwin', writable: true });
|
|
121
|
+
process.env.SHELL = '/bin/zsh';
|
|
122
|
+
expect(isMacOS()).toBe(true);
|
|
123
|
+
expect(getDefaultShell()).toBe('/bin/zsh');
|
|
124
|
+
});
|
|
125
|
+
it('Linux + Bash 应该正常工作', () => {
|
|
126
|
+
Object.defineProperty(process, 'platform', { value: 'linux', writable: true });
|
|
127
|
+
process.env.SHELL = '/bin/bash';
|
|
128
|
+
expect(isLinux()).toBe(true);
|
|
129
|
+
expect(getDefaultShell()).toBe('/bin/bash');
|
|
130
|
+
});
|
|
131
|
+
it('Windows + PowerShell 7 应该正常工作', () => {
|
|
132
|
+
Object.defineProperty(process, 'platform', { value: 'win32', writable: true });
|
|
133
|
+
process.env.PSModulePath = 'C:\\Program Files\\PowerShell\\7\\Modules';
|
|
134
|
+
expect(isWindows()).toBe(true);
|
|
135
|
+
expect(getDefaultShell()).toBe('pwsh.exe');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 跨平台工具函数
|
|
3
|
+
* 封装所有平台相关的逻辑,优先支持 macOS/Linux,兼容 Windows
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Shell 类型
|
|
7
|
+
*/
|
|
8
|
+
export type ShellType = 'zsh' | 'bash' | 'fish' | 'cmd' | 'powershell5' | 'powershell7' | 'unknown';
|
|
9
|
+
/**
|
|
10
|
+
* Shell 能力
|
|
11
|
+
*/
|
|
12
|
+
export interface ShellCapabilities {
|
|
13
|
+
/** 是否支持 Hook(修改配置文件) */
|
|
14
|
+
supportsHook: boolean;
|
|
15
|
+
/** 是否支持历史读取 */
|
|
16
|
+
supportsHistory: boolean;
|
|
17
|
+
/** 配置文件路径 */
|
|
18
|
+
configPath: string | null;
|
|
19
|
+
/** 历史文件路径 */
|
|
20
|
+
historyPath: string | null;
|
|
21
|
+
/** 用于执行命令的 Shell 可执行文件 */
|
|
22
|
+
executable: string;
|
|
23
|
+
/** Shell 名称(用于显示) */
|
|
24
|
+
displayName: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 是否为 Windows 平台
|
|
28
|
+
*/
|
|
29
|
+
export declare function isWindows(): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* 是否为 macOS 平台
|
|
32
|
+
*/
|
|
33
|
+
export declare function isMacOS(): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* 是否为 Linux 平台
|
|
36
|
+
*/
|
|
37
|
+
export declare function isLinux(): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* 检测当前 Shell 类型
|
|
40
|
+
*/
|
|
41
|
+
export declare function detectShell(): ShellType;
|
|
42
|
+
/**
|
|
43
|
+
* 获取 Shell 能力信息
|
|
44
|
+
*/
|
|
45
|
+
export declare function getShellCapabilities(shell: ShellType): ShellCapabilities;
|
|
46
|
+
/**
|
|
47
|
+
* 检测命令是否存在(跨平台)
|
|
48
|
+
*/
|
|
49
|
+
export declare function commandExists(command: string): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* 批量检测命令是否存在(优化性能)
|
|
52
|
+
* @returns 返回存在的命令列表
|
|
53
|
+
*/
|
|
54
|
+
export declare function batchCommandExists(commands: string[]): string[];
|
|
55
|
+
/**
|
|
56
|
+
* 命令执行配置
|
|
57
|
+
*/
|
|
58
|
+
export interface ShellExecConfig {
|
|
59
|
+
/** Shell 可执行文件 */
|
|
60
|
+
shell: string;
|
|
61
|
+
/** Shell 参数 */
|
|
62
|
+
args: string[];
|
|
63
|
+
/** 完整的命令字符串(已包含错误处理) */
|
|
64
|
+
command: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 构建命令执行配置
|
|
68
|
+
* 处理不同 Shell 的语法差异
|
|
69
|
+
*/
|
|
70
|
+
export declare function buildShellExecConfig(command: string, shell?: ShellType): ShellExecConfig;
|
|
71
|
+
/**
|
|
72
|
+
* 获取默认 Shell(用于交互式执行)
|
|
73
|
+
*/
|
|
74
|
+
export declare function getDefaultShell(): string;
|
|
75
|
+
/**
|
|
76
|
+
* 获取 pls 配置目录
|
|
77
|
+
* 统一使用 ~/.please
|
|
78
|
+
*/
|
|
79
|
+
export declare function getConfigDir(): string;
|
|
80
|
+
/**
|
|
81
|
+
* 将路径转换为当前平台格式
|
|
82
|
+
*/
|
|
83
|
+
export declare function normalizePath(p: string): string;
|
|
84
|
+
/**
|
|
85
|
+
* 获取用于 PowerShell 脚本中的路径
|
|
86
|
+
* 使用 $env:USERPROFILE 而不是硬编码路径
|
|
87
|
+
*/
|
|
88
|
+
export declare function getPowerShellConfigDir(): string;
|