@lobehub/lobehub 2.0.0-next.51 → 2.0.0-next.52

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 (84) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/apps/desktop/src/main/controllers/LocalFileCtr.ts +25 -5
  3. package/apps/desktop/src/main/controllers/__tests__/LocalFileCtr.test.ts +4 -1
  4. package/apps/desktop/src/main/modules/fileSearch/__tests__/macOS.integration.test.ts +357 -0
  5. package/apps/desktop/src/main/modules/fileSearch/impl/macOS.ts +30 -22
  6. package/changelog/v1.json +9 -0
  7. package/locales/ar/models.json +119 -126
  8. package/locales/ar/plugin.json +1 -1
  9. package/locales/bg-BG/models.json +104 -132
  10. package/locales/bg-BG/plugin.json +1 -1
  11. package/locales/de-DE/models.json +119 -126
  12. package/locales/de-DE/plugin.json +1 -1
  13. package/locales/en-US/models.json +167 -126
  14. package/locales/en-US/plugin.json +1 -1
  15. package/locales/es-ES/models.json +119 -126
  16. package/locales/es-ES/plugin.json +1 -1
  17. package/locales/fa-IR/models.json +119 -126
  18. package/locales/fa-IR/plugin.json +1 -1
  19. package/locales/fr-FR/models.json +119 -126
  20. package/locales/fr-FR/plugin.json +1 -1
  21. package/locales/it-IT/models.json +119 -126
  22. package/locales/it-IT/plugin.json +1 -1
  23. package/locales/ja-JP/models.json +119 -126
  24. package/locales/ja-JP/plugin.json +1 -1
  25. package/locales/ko-KR/models.json +119 -126
  26. package/locales/ko-KR/plugin.json +1 -1
  27. package/locales/nl-NL/models.json +119 -126
  28. package/locales/nl-NL/plugin.json +1 -1
  29. package/locales/pl-PL/models.json +119 -126
  30. package/locales/pl-PL/plugin.json +1 -1
  31. package/locales/pt-BR/models.json +119 -126
  32. package/locales/pt-BR/plugin.json +1 -1
  33. package/locales/ru-RU/models.json +119 -126
  34. package/locales/ru-RU/plugin.json +1 -1
  35. package/locales/tr-TR/models.json +119 -126
  36. package/locales/tr-TR/plugin.json +1 -1
  37. package/locales/vi-VN/models.json +119 -126
  38. package/locales/vi-VN/plugin.json +1 -1
  39. package/locales/zh-CN/models.json +173 -80
  40. package/locales/zh-CN/plugin.json +1 -1
  41. package/locales/zh-TW/models.json +119 -126
  42. package/locales/zh-TW/plugin.json +1 -1
  43. package/package.json +1 -1
  44. package/packages/electron-client-ipc/src/types/localSystem.ts +26 -2
  45. package/packages/model-runtime/src/core/contextBuilders/openai.test.ts +58 -0
  46. package/packages/model-runtime/src/core/contextBuilders/openai.ts +24 -10
  47. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +3 -2
  48. package/packages/model-runtime/src/providers/openai/index.test.ts +44 -0
  49. package/packages/types/src/tool/builtin.ts +6 -4
  50. package/src/features/Conversation/Messages/Assistant/Tool/Render/LoadingPlaceholder/index.tsx +3 -3
  51. package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/index.tsx +2 -2
  52. package/src/features/Conversation/Messages/Group/Tool/Render/LoadingPlaceholder/index.tsx +3 -3
  53. package/src/features/PluginsUI/Render/BuiltinType/index.test.tsx +10 -4
  54. package/src/features/PluginsUI/Render/BuiltinType/index.tsx +2 -2
  55. package/src/locales/default/plugin.ts +1 -1
  56. package/src/services/chat/chat.test.ts +1 -0
  57. package/src/store/aiInfra/slices/aiProvider/__tests__/selectors.test.ts +62 -0
  58. package/src/store/aiInfra/slices/aiProvider/selectors.ts +1 -1
  59. package/src/tools/code-interpreter/Render/index.tsx +1 -1
  60. package/src/tools/interventions.ts +28 -4
  61. package/src/tools/local-system/Placeholder/ListFiles.tsx +3 -5
  62. package/src/tools/local-system/Placeholder/SearchFiles.tsx +2 -5
  63. package/src/tools/local-system/Render/ListFiles/index.tsx +16 -21
  64. package/src/tools/local-system/Render/RenameLocalFile/index.tsx +15 -20
  65. package/src/tools/local-system/Render/RunCommand/index.tsx +67 -70
  66. package/src/tools/local-system/Render/SearchFiles/SearchQuery/index.tsx +0 -1
  67. package/src/tools/local-system/Render/SearchFiles/index.tsx +15 -20
  68. package/src/tools/local-system/Render/WriteFile/index.tsx +2 -8
  69. package/src/tools/local-system/index.ts +4 -4
  70. package/src/tools/local-system/systemRole.ts +1 -1
  71. package/src/tools/placeholders.ts +39 -8
  72. package/src/tools/renders.ts +56 -9
  73. package/src/tools/web-browsing/Placeholder/{PageContent.tsx → CrawlMultiPages.tsx} +4 -1
  74. package/src/tools/web-browsing/Placeholder/CrawlSinglePage.tsx +12 -0
  75. package/src/tools/web-browsing/Placeholder/Search.tsx +4 -4
  76. package/src/tools/web-browsing/Render/CrawlMultiPages.tsx +15 -0
  77. package/src/tools/web-browsing/Render/CrawlSinglePage.tsx +15 -0
  78. package/src/tools/web-browsing/Render/Search/index.tsx +39 -44
  79. package/packages/database/migrations/0044_add_tool_intervention.sql +0 -1
  80. package/src/tools/local-system/Intervention/index.tsx +0 -17
  81. package/src/tools/local-system/Placeholder/index.tsx +0 -25
  82. package/src/tools/local-system/Render/index.tsx +0 -42
  83. package/src/tools/web-browsing/Placeholder/index.tsx +0 -40
  84. package/src/tools/web-browsing/Render/index.tsx +0 -57
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.52](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.51...v2.0.0-next.52)
6
+
7
+ <sup>Released on **2025-11-13**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Filter out reasoning fields from messages in ChatCompletion API.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Filter out reasoning fields from messages in ChatCompletion API, closes [#10203](https://github.com/lobehub/lobe-chat/issues/10203) [#10193](https://github.com/lobehub/lobe-chat/issues/10193) ([5f28b2c](https://github.com/lobehub/lobe-chat/commit/5f28b2c))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ## [Version 2.0.0-next.51](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.50...v2.0.0-next.51)
6
31
 
7
32
  <sup>Released on **2025-11-13**</sup>
@@ -467,15 +467,35 @@ export default class LocalFileCtr extends ControllerModule {
467
467
  */
468
468
  @ipcClientEvent('searchLocalFiles')
469
469
  async handleLocalFilesSearch(params: LocalSearchFilesParams): Promise<FileResult[]> {
470
- logger.debug('Received file search request:', { keywords: params.keywords });
470
+ logger.debug('Received file search request:', {
471
+ directory: params.directory,
472
+ keywords: params.keywords,
473
+ });
471
474
 
472
- const options: Omit<SearchOptions, 'keywords'> = {
473
- limit: 30,
475
+ // Build search options from params, mapping directory to onlyIn
476
+ const options: SearchOptions = {
477
+ contentContains: params.contentContains,
478
+ createdAfter: params.createdAfter ? new Date(params.createdAfter) : undefined,
479
+ createdBefore: params.createdBefore ? new Date(params.createdBefore) : undefined,
480
+ detailed: params.detailed,
481
+ exclude: params.exclude,
482
+ fileTypes: params.fileTypes,
483
+ keywords: params.keywords,
484
+ limit: params.limit || 30,
485
+ liveUpdate: params.liveUpdate,
486
+ modifiedAfter: params.modifiedAfter ? new Date(params.modifiedAfter) : undefined,
487
+ modifiedBefore: params.modifiedBefore ? new Date(params.modifiedBefore) : undefined,
488
+ onlyIn: params.directory, // Map directory param to onlyIn option
489
+ sortBy: params.sortBy,
490
+ sortDirection: params.sortDirection,
474
491
  };
475
492
 
476
493
  try {
477
- const results = await this.searchService.search(params.keywords, options);
478
- logger.debug('File search completed', { count: results.length });
494
+ const results = await this.searchService.search(options.keywords, options);
495
+ logger.debug('File search completed', {
496
+ count: results.length,
497
+ directory: params.directory,
498
+ });
479
499
  return results;
480
500
  } catch (error) {
481
501
  logger.error('File search failed:', error);
@@ -345,7 +345,10 @@ describe('LocalFileCtr', () => {
345
345
  const result = await localFileCtr.handleLocalFilesSearch({ keywords: 'test' });
346
346
 
347
347
  expect(result).toEqual(mockResults);
348
- expect(mockSearchService.search).toHaveBeenCalledWith('test', { limit: 30 });
348
+ expect(mockSearchService.search).toHaveBeenCalledWith('test', {
349
+ keywords: 'test',
350
+ limit: 30,
351
+ });
349
352
  });
350
353
 
351
354
  it('should return empty array on search error', async () => {
@@ -0,0 +1,357 @@
1
+ import path from 'node:path';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ import { MacOSSearchServiceImpl } from '../impl/macOS';
5
+
6
+ /**
7
+ * macOS File Search Integration Tests
8
+ *
9
+ * These tests run against the real macOS Spotlight service
10
+ * using files in the current repository.
11
+ *
12
+ * Run with: bunx vitest run 'macOS.integration.test'
13
+ */
14
+
15
+ // Get repository root path (assumes test runs from apps/desktop)
16
+ const repoRoot = path.resolve(__dirname, '../../../../..');
17
+
18
+ describe.skipIf(process.platform !== 'darwin')('MacOSSearchServiceImpl Integration', () => {
19
+ const searchService = new MacOSSearchServiceImpl();
20
+
21
+ describe('checkSearchServiceStatus', () => {
22
+ it('should verify Spotlight is available on macOS', async () => {
23
+ const isAvailable = await searchService.checkSearchServiceStatus();
24
+
25
+ expect(isAvailable).toBe(true);
26
+ });
27
+ });
28
+
29
+ describe('search for known repository files', () => {
30
+ it('should find package.json in repo root', async () => {
31
+ const results = await searchService.search({
32
+ keywords: 'package.json',
33
+ limit: 10,
34
+ onlyIn: repoRoot,
35
+ });
36
+
37
+ expect(results.length).toBeGreaterThan(0);
38
+
39
+ // Should find at least one package.json
40
+ const packageJson = results.find((r) => r.name === 'package.json');
41
+ expect(packageJson).toBeDefined();
42
+ expect(packageJson!.type).toBe('json');
43
+ expect(packageJson!.path).toContain(repoRoot);
44
+ });
45
+
46
+ it('should find README files', async () => {
47
+ const results = await searchService.search({
48
+ keywords: 'README',
49
+ limit: 10,
50
+ onlyIn: repoRoot,
51
+ });
52
+ expect(results.length).toBeGreaterThan(0);
53
+
54
+ // Should contain markdown files
55
+ const mdFile = results.find((r) => r.type === 'md');
56
+ expect(mdFile).toBeDefined();
57
+ expect(mdFile!.name).toMatch(/README/i);
58
+ });
59
+
60
+ it('should find TypeScript files', async () => {
61
+ const results = await searchService.search({
62
+ keywords: 'macOS',
63
+ limit: 10,
64
+ onlyIn: repoRoot,
65
+ });
66
+
67
+ expect(results.length).toBeGreaterThan(0);
68
+
69
+ // Should find the macOS.ts implementation file
70
+ const macOSFile = results.find((r) => r.name.includes('macOS') && r.type === 'ts');
71
+ expect(macOSFile).toBeDefined();
72
+ expect(macOSFile!.contentType).toBe('code');
73
+ });
74
+
75
+ it('should find files in apps/desktop directory', async () => {
76
+ const desktopPath = path.join(repoRoot, 'apps/desktop');
77
+
78
+ const results = await searchService.search({
79
+ keywords: 'src',
80
+ limit: 20,
81
+ onlyIn: desktopPath,
82
+ });
83
+
84
+ // Spotlight indexing may not be complete for this directory
85
+ // so we make the test lenient
86
+ if (results.length > 0) {
87
+ // All results should be within apps/desktop
88
+ results.forEach((result) => {
89
+ expect(result.path).toContain('apps/desktop');
90
+ });
91
+ } else {
92
+ // eslint-disable-next-line no-console
93
+ console.warn(
94
+ '⚠️ No results found in apps/desktop - Spotlight indexing may not be complete',
95
+ );
96
+ }
97
+
98
+ // At minimum, verify the search completed without error
99
+ expect(Array.isArray(results)).toBe(true);
100
+ });
101
+
102
+ it('should find test files', async () => {
103
+ const results = await searchService.search({
104
+ keywords: 'test.ts',
105
+ limit: 10,
106
+ onlyIn: repoRoot,
107
+ });
108
+
109
+ expect(results.length).toBeGreaterThan(0);
110
+
111
+ // Should find test files
112
+ const testFile = results.find((r) => r.name.endsWith('.test.ts'));
113
+ expect(testFile).toBeDefined();
114
+ expect(testFile!.path).toContain('__tests__');
115
+ });
116
+ });
117
+
118
+ describe('search with filters', () => {
119
+ it('should respect limit parameter', async () => {
120
+ const limit = 3;
121
+ const results = await searchService.search({
122
+ keywords: 'src',
123
+ limit,
124
+ onlyIn: repoRoot,
125
+ });
126
+
127
+ expect(results.length).toBeLessThanOrEqual(limit);
128
+ });
129
+
130
+ it('should search in specific subdirectory only', async () => {
131
+ const srcPath = path.join(repoRoot, 'apps/desktop/src');
132
+
133
+ const results = await searchService.search({
134
+ keywords: 'index',
135
+ limit: 10,
136
+ onlyIn: srcPath,
137
+ });
138
+
139
+ // All results should be within the specified directory
140
+ results.forEach((result) => {
141
+ expect(result.path).toContain('apps/desktop/src');
142
+ });
143
+ });
144
+
145
+ it('should return empty array for non-existent keywords', async () => {
146
+ const results = await searchService.search({
147
+ keywords: 'xyzabc123unlikely-keyword-that-does-not-exist-12345',
148
+ limit: 5,
149
+ onlyIn: repoRoot,
150
+ });
151
+
152
+ expect(results).toEqual([]);
153
+ });
154
+ });
155
+
156
+ describe('file type detection', () => {
157
+ it('should correctly identify TypeScript files', async () => {
158
+ const results = await searchService.search({
159
+ keywords: 'LocalFileCtr',
160
+ limit: 5,
161
+ onlyIn: repoRoot,
162
+ });
163
+
164
+ const tsFile = results.find((r) => r.name === 'LocalFileCtr.ts');
165
+ if (tsFile) {
166
+ expect(tsFile.type).toBe('ts');
167
+ expect(tsFile.contentType).toBe('code');
168
+ expect(tsFile.isDirectory).toBe(false);
169
+ }
170
+ });
171
+
172
+ it('should correctly identify JSON files', async () => {
173
+ const results = await searchService.search({
174
+ keywords: 'tsconfig',
175
+ limit: 5,
176
+ onlyIn: repoRoot,
177
+ });
178
+
179
+ const jsonFile = results.find((r) => r.name.includes('tsconfig') && r.type === 'json');
180
+ if (jsonFile) {
181
+ expect(jsonFile.type).toBe('json');
182
+ expect(jsonFile.contentType).toBe('code');
183
+ expect(jsonFile.size).toBeGreaterThan(0);
184
+ }
185
+ });
186
+
187
+ it('should correctly identify directories', async () => {
188
+ const results = await searchService.search({
189
+ keywords: '__tests__',
190
+ limit: 10,
191
+ onlyIn: repoRoot,
192
+ });
193
+
194
+ const testDir = results.find((r) => r.name === '__tests__' && r.isDirectory);
195
+ if (testDir) {
196
+ expect(testDir.isDirectory).toBe(true);
197
+ expect(testDir.type).toBe('');
198
+ }
199
+ });
200
+
201
+ it('should correctly identify markdown files', async () => {
202
+ const results = await searchService.search({
203
+ keywords: 'CLAUDE.md',
204
+ limit: 5,
205
+ onlyIn: repoRoot,
206
+ });
207
+
208
+ const mdFile = results.find((r) => r.name === 'CLAUDE.md');
209
+ if (mdFile) {
210
+ expect(mdFile.type).toBe('md');
211
+ expect(mdFile.contentType).toBe('text');
212
+ }
213
+ });
214
+ });
215
+
216
+ describe('file metadata', () => {
217
+ it('should return valid file metadata', async () => {
218
+ const results = await searchService.search({
219
+ keywords: 'package.json',
220
+ limit: 1,
221
+ onlyIn: repoRoot,
222
+ });
223
+
224
+ expect(results.length).toBeGreaterThan(0);
225
+
226
+ const file = results[0];
227
+
228
+ // Verify all metadata fields are present
229
+ expect(file.path).toBeTruthy();
230
+ expect(file.name).toBeTruthy();
231
+ expect(typeof file.isDirectory).toBe('boolean');
232
+ expect(typeof file.size).toBe('number');
233
+ expect(file.size).toBeGreaterThanOrEqual(0);
234
+ expect(file.type).toBeDefined();
235
+ expect(file.contentType).toBeDefined();
236
+ expect(file.modifiedTime).toBeInstanceOf(Date);
237
+ expect(file.createdTime).toBeInstanceOf(Date);
238
+ expect(file.lastAccessTime).toBeInstanceOf(Date);
239
+
240
+ // Dates should be valid
241
+ expect(file.modifiedTime.getTime()).toBeGreaterThan(0);
242
+ expect(file.createdTime.getTime()).toBeGreaterThan(0);
243
+ });
244
+
245
+ it('should handle files with different extensions', async () => {
246
+ const testCases = [
247
+ { keyword: '.ts', expectedType: 'ts', expectedContentType: 'code' },
248
+ { keyword: '.json', expectedType: 'json', expectedContentType: 'code' },
249
+ { keyword: '.txt', expectedType: 'txt', expectedContentType: 'text' },
250
+ ];
251
+
252
+ for (const { keyword, expectedType, expectedContentType } of testCases) {
253
+ const results = await searchService.search({
254
+ keywords: keyword,
255
+ limit: 5,
256
+ onlyIn: repoRoot,
257
+ });
258
+
259
+ if (results.length > 0) {
260
+ const file = results.find((r) => r.type === expectedType);
261
+ if (file) {
262
+ expect(file.type).toBe(expectedType);
263
+ expect(file.contentType).toBe(expectedContentType);
264
+ }
265
+ }
266
+ }
267
+ });
268
+ });
269
+
270
+ describe('search accuracy after fix', () => {
271
+ it('should use fuzzy matching instead of exact phrase', async () => {
272
+ // Test the fix: keywords should do fuzzy matching, not exact phrase
273
+ // Before fix: "local file" would only match exact phrase "local file"
274
+ // After fix: "local file" should match "LocalFileCtr" (contains "local" and "file")
275
+
276
+ const results = await searchService.search({
277
+ keywords: 'LocalFile',
278
+ limit: 10,
279
+ onlyIn: repoRoot,
280
+ });
281
+
282
+ expect(results.length).toBeGreaterThan(0);
283
+
284
+ // Should find LocalFileCtr.ts or similar files
285
+ const found = results.some(
286
+ (r) => r.name.includes('LocalFile') || r.name.includes('localFile'),
287
+ );
288
+ expect(found).toBe(true);
289
+ });
290
+
291
+ it('should handle paths with spaces correctly', async () => {
292
+ // Test the fix: command args should be properly split
293
+ // This test verifies spawn receives correct arguments array
294
+
295
+ const pathWithSpaces = repoRoot; // May contain spaces in CI or certain setups
296
+ const results = await searchService.search({
297
+ keywords: 'test',
298
+ limit: 5,
299
+ onlyIn: pathWithSpaces,
300
+ });
301
+
302
+ // Should not throw error even if path contains spaces
303
+ expect(Array.isArray(results)).toBe(true);
304
+ });
305
+
306
+ it('should search case-insensitively', async () => {
307
+ // The "cd" flag in kMDItemFSName makes it case-insensitive
308
+
309
+ const lowerResults = await searchService.search({
310
+ keywords: 'readme',
311
+ limit: 5,
312
+ onlyIn: repoRoot,
313
+ });
314
+
315
+ const upperResults = await searchService.search({
316
+ keywords: 'README',
317
+ limit: 5,
318
+ onlyIn: repoRoot,
319
+ });
320
+
321
+ // Both searches should find similar files
322
+ expect(lowerResults.length).toBeGreaterThan(0);
323
+ expect(upperResults.length).toBeGreaterThan(0);
324
+ });
325
+ });
326
+
327
+ describe('error handling', () => {
328
+ it('should handle non-existent directory gracefully', async () => {
329
+ const nonExistentPath = path.join(repoRoot, 'this-directory-does-not-exist-12345');
330
+
331
+ const results = await searchService.search({
332
+ keywords: 'test',
333
+ limit: 5,
334
+ onlyIn: nonExistentPath,
335
+ });
336
+
337
+ // Should return empty array instead of throwing
338
+ expect(results).toEqual([]);
339
+ });
340
+ });
341
+
342
+ describe('updateSearchIndex', () => {
343
+ it.skip('should handle index update request', async () => {
344
+ // Index update requires elevated permissions, may fail in restricted environments
345
+ const result = await searchService.updateSearchIndex(repoRoot);
346
+
347
+ // Should return boolean (true if succeeded, false if failed)
348
+ expect(typeof result).toBe('boolean');
349
+ }, 15000); // Index update can take time
350
+ });
351
+ });
352
+
353
+ // Skip message for non-macOS platforms
354
+ if (process.platform !== 'darwin') {
355
+ // eslint-disable-next-line no-console
356
+ console.log('⏭️ Skipping macOS integration tests on', process.platform, '(only runs on darwin)');
357
+ }
@@ -23,12 +23,11 @@ export class MacOSSearchServiceImpl extends FileSearchImpl {
23
23
  */
24
24
  async search(options: SearchOptions): Promise<FileResult[]> {
25
25
  // Build the command first, regardless of execution method
26
- const command = this.buildSearchCommand(options);
27
- logger.debug(`Executing command: ${command}`);
26
+ const { cmd, args, commandString } = this.buildSearchCommand(options);
27
+ logger.debug(`Executing command: ${commandString}`);
28
28
 
29
29
  // Use spawn for both live and non-live updates to handle large outputs
30
30
  return new Promise((resolve, reject) => {
31
- const [cmd, ...args] = command.split(' ');
32
31
  const childProcess = spawn(cmd, args);
33
32
 
34
33
  let results: string[] = []; // Store raw file paths
@@ -137,31 +136,39 @@ export class MacOSSearchServiceImpl extends FileSearchImpl {
137
136
  /**
138
137
  * Build mdfind command string
139
138
  * @param options Search options
140
- * @returns Complete command string
139
+ * @returns Command components (cmd, args array, and command string for logging)
141
140
  */
142
- private buildSearchCommand(options: SearchOptions): string {
143
- // Basic command
144
- let command = 'mdfind';
145
-
146
- // Add options
147
- const mdFindOptions: string[] = [];
141
+ private buildSearchCommand(options: SearchOptions): {
142
+ args: string[];
143
+ cmd: string;
144
+ commandString: string;
145
+ } {
146
+ // Command and arguments array
147
+ const cmd = 'mdfind';
148
+ const args: string[] = [];
148
149
 
149
150
  // macOS mdfind doesn't support -limit parameter, we'll limit results in post-processing
150
151
 
151
152
  // Search in specific directory
152
153
  if (options.onlyIn) {
153
- mdFindOptions.push(`-onlyin "${options.onlyIn}"`);
154
+ args.push('-onlyin', options.onlyIn);
154
155
  }
155
156
 
156
157
  // Live update
157
158
  if (options.liveUpdate) {
158
- mdFindOptions.push('-live');
159
+ args.push('-live');
159
160
  }
160
161
 
161
162
  // Detailed metadata
162
163
  if (options.detailed) {
163
- mdFindOptions.push(
164
- '-attr kMDItemDisplayName kMDItemContentType kMDItemKind kMDItemFSSize kMDItemFSCreationDate kMDItemFSContentChangeDate',
164
+ args.push(
165
+ '-attr',
166
+ 'kMDItemDisplayName',
167
+ 'kMDItemContentType',
168
+ 'kMDItemKind',
169
+ 'kMDItemFSSize',
170
+ 'kMDItemFSCreationDate',
171
+ 'kMDItemFSContentChangeDate',
165
172
  );
166
173
  }
167
174
 
@@ -171,9 +178,10 @@ export class MacOSSearchServiceImpl extends FileSearchImpl {
171
178
  // Basic query
172
179
  if (options.keywords) {
173
180
  // If the query string doesn't use Spotlight query syntax (doesn't contain kMDItem properties),
174
- // treat it as plain text search
181
+ // treat it as a flexible name search rather than exact phrase match
175
182
  if (!options.keywords.includes('kMDItem')) {
176
- queryExpression = `"${options.keywords.replaceAll('"', '\\"')}"`;
183
+ // Use kMDItemFSName for filename matching with wildcards for better flexibility
184
+ queryExpression = `kMDItemFSName == "*${options.keywords.replaceAll('"', '\\"')}*"cd`;
177
185
  } else {
178
186
  queryExpression = options.keywords;
179
187
  }
@@ -244,15 +252,15 @@ export class MacOSSearchServiceImpl extends FileSearchImpl {
244
252
  }
245
253
  }
246
254
 
247
- // Combine complete command
248
- if (mdFindOptions.length > 0) {
249
- command += ' ' + mdFindOptions.join(' ');
255
+ // Add query expression to args
256
+ if (queryExpression) {
257
+ args.push(queryExpression);
250
258
  }
251
259
 
252
- // Finally add query expression
253
- command += ` ${queryExpression}`;
260
+ // Build command string for logging
261
+ const commandString = `${cmd} ${args.map((arg) => (arg.includes(' ') || arg.includes('*') ? `"${arg}"` : arg)).join(' ')}`;
254
262
 
255
- return command;
263
+ return { args, cmd, commandString };
256
264
  }
257
265
 
258
266
  /**
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Filter out reasoning fields from messages in ChatCompletion API."
6
+ ]
7
+ },
8
+ "date": "2025-11-13",
9
+ "version": "2.0.0-next.52"
10
+ },
2
11
  {
3
12
  "children": {
4
13
  "improvements": [