@vybestack/llxprt-code-core 0.1.13 → 0.1.14-nightly.250729.2076f7c6

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 (218) hide show
  1. package/README.md +24 -0
  2. package/dist/src/code_assist/codeAssist.d.ts +2 -1
  3. package/dist/src/code_assist/codeAssist.js +4 -3
  4. package/dist/src/code_assist/codeAssist.js.map +1 -1
  5. package/dist/src/code_assist/oauth2.js +9 -2
  6. package/dist/src/code_assist/oauth2.js.map +1 -1
  7. package/dist/src/code_assist/oauth2.test.js +94 -2
  8. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  9. package/dist/src/code_assist/server.d.ts +5 -6
  10. package/dist/src/code_assist/server.js +7 -70
  11. package/dist/src/code_assist/server.js.map +1 -1
  12. package/dist/src/code_assist/setup.d.ts +6 -1
  13. package/dist/src/code_assist/setup.js +4 -1
  14. package/dist/src/code_assist/setup.js.map +1 -1
  15. package/dist/src/code_assist/setup.test.js +4 -1
  16. package/dist/src/code_assist/setup.test.js.map +1 -1
  17. package/dist/src/code_assist/types.d.ts +2 -2
  18. package/dist/src/config/config.d.ts +25 -3
  19. package/dist/src/config/config.js +47 -10
  20. package/dist/src/config/config.js.map +1 -1
  21. package/dist/src/config/config.test.js +1 -26
  22. package/dist/src/config/config.test.js.map +1 -1
  23. package/dist/src/config/flashFallback.test.js +1 -1
  24. package/dist/src/config/flashFallback.test.js.map +1 -1
  25. package/dist/src/core/client.d.ts +6 -2
  26. package/dist/src/core/client.js +41 -18
  27. package/dist/src/core/client.js.map +1 -1
  28. package/dist/src/core/client.test.js +78 -24
  29. package/dist/src/core/client.test.js.map +1 -1
  30. package/dist/src/core/contentGenerator.d.ts +1 -1
  31. package/dist/src/core/contentGenerator.js +1 -1
  32. package/dist/src/core/contentGenerator.js.map +1 -1
  33. package/dist/src/core/coreToolScheduler.js +14 -1
  34. package/dist/src/core/coreToolScheduler.js.map +1 -1
  35. package/dist/src/core/coreToolScheduler.test.js +80 -0
  36. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  37. package/dist/src/core/geminiChat.d.ts +4 -3
  38. package/dist/src/core/geminiChat.js +8 -11
  39. package/dist/src/core/geminiChat.js.map +1 -1
  40. package/dist/src/core/geminiRequest.js +2 -37
  41. package/dist/src/core/geminiRequest.js.map +1 -1
  42. package/dist/src/core/logger.js +6 -0
  43. package/dist/src/core/logger.js.map +1 -1
  44. package/dist/src/core/nonInteractiveToolExecutor.test.js +5 -5
  45. package/dist/src/core/prompts.js +42 -18
  46. package/dist/src/core/prompts.js.map +1 -1
  47. package/dist/src/core/prompts.test.js +121 -4
  48. package/dist/src/core/prompts.test.js.map +1 -1
  49. package/dist/src/core/turn.d.ts +7 -2
  50. package/dist/src/core/turn.js +9 -0
  51. package/dist/src/core/turn.js.map +1 -1
  52. package/dist/src/core/turn.test.js +129 -0
  53. package/dist/src/core/turn.test.js.map +1 -1
  54. package/dist/src/ide/ide-client.d.ts +28 -0
  55. package/dist/src/ide/ide-client.js +79 -0
  56. package/dist/src/ide/ide-client.js.map +1 -0
  57. package/dist/src/ide/ideContext.d.ts +174 -0
  58. package/dist/src/{services → ide}/ideContext.js +28 -25
  59. package/dist/src/ide/ideContext.js.map +1 -0
  60. package/dist/src/{services → ide}/ideContext.test.js +39 -39
  61. package/dist/src/ide/ideContext.test.js.map +1 -0
  62. package/dist/src/index.d.ts +3 -1
  63. package/dist/src/index.js +4 -1
  64. package/dist/src/index.js.map +1 -1
  65. package/dist/src/mcp/google-auth-provider.d.ts +23 -0
  66. package/dist/src/mcp/google-auth-provider.js +63 -0
  67. package/dist/src/mcp/google-auth-provider.js.map +1 -0
  68. package/dist/src/mcp/google-auth-provider.test.js +54 -0
  69. package/dist/src/mcp/google-auth-provider.test.js.map +1 -0
  70. package/dist/src/providers/anthropic/AnthropicProvider.js.map +1 -1
  71. package/dist/src/providers/gemini/GeminiProvider.d.ts +2 -0
  72. package/dist/src/providers/gemini/GeminiProvider.integration.test.js +36 -0
  73. package/dist/src/providers/gemini/GeminiProvider.integration.test.js.map +1 -1
  74. package/dist/src/providers/gemini/GeminiProvider.js +57 -10
  75. package/dist/src/providers/gemini/GeminiProvider.js.map +1 -1
  76. package/dist/src/providers/openai/OpenAIProvider.js +10 -7
  77. package/dist/src/providers/openai/OpenAIProvider.js.map +1 -1
  78. package/dist/src/services/fileDiscoveryService.d.ts +4 -4
  79. package/dist/src/services/fileDiscoveryService.js +8 -8
  80. package/dist/src/services/gitService.js +1 -1
  81. package/dist/src/services/gitService.js.map +1 -1
  82. package/dist/src/services/gitService.test.js +1 -1
  83. package/dist/src/services/gitService.test.js.map +1 -1
  84. package/dist/src/services/loopDetectionService.d.ts +48 -5
  85. package/dist/src/services/loopDetectionService.js +127 -41
  86. package/dist/src/services/loopDetectionService.js.map +1 -1
  87. package/dist/src/services/loopDetectionService.test.js +50 -123
  88. package/dist/src/services/loopDetectionService.test.js.map +1 -1
  89. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +2 -1
  90. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +17 -2
  91. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  92. package/dist/src/telemetry/constants.d.ts +16 -15
  93. package/dist/src/telemetry/constants.js +16 -15
  94. package/dist/src/telemetry/constants.js.map +1 -1
  95. package/dist/src/telemetry/file-exporters.d.ts +28 -0
  96. package/dist/src/telemetry/file-exporters.js +62 -0
  97. package/dist/src/telemetry/file-exporters.js.map +1 -0
  98. package/dist/src/telemetry/loggers.d.ts +2 -1
  99. package/dist/src/telemetry/loggers.js +18 -3
  100. package/dist/src/telemetry/loggers.js.map +1 -1
  101. package/dist/src/telemetry/sdk.js +22 -7
  102. package/dist/src/telemetry/sdk.js.map +1 -1
  103. package/dist/src/telemetry/types.d.ts +9 -2
  104. package/dist/src/telemetry/types.js +13 -1
  105. package/dist/src/telemetry/types.js.map +1 -1
  106. package/dist/src/tools/ToolFormatter.js +19 -1
  107. package/dist/src/tools/ToolFormatter.js.map +1 -1
  108. package/dist/src/tools/edit.js +10 -4
  109. package/dist/src/tools/edit.js.map +1 -1
  110. package/dist/src/tools/edit.test.js +12 -0
  111. package/dist/src/tools/edit.test.js.map +1 -1
  112. package/dist/src/tools/grep.test.js +1 -1
  113. package/dist/src/tools/grep.test.js.map +1 -1
  114. package/dist/src/tools/ls.d.ts +5 -2
  115. package/dist/src/tools/ls.js +39 -10
  116. package/dist/src/tools/ls.js.map +1 -1
  117. package/dist/src/tools/mcp-client.d.ts +5 -1
  118. package/dist/src/tools/mcp-client.js +416 -29
  119. package/dist/src/tools/mcp-client.js.map +1 -1
  120. package/dist/src/tools/mcp-client.test.js +46 -6
  121. package/dist/src/tools/mcp-client.test.js.map +1 -1
  122. package/dist/src/tools/mcp-tool.js +1 -1
  123. package/dist/src/tools/mcp-tool.js.map +1 -1
  124. package/dist/src/tools/mcp-tool.test.js +34 -0
  125. package/dist/src/tools/mcp-tool.test.js.map +1 -1
  126. package/dist/src/tools/read-file.js +1 -1
  127. package/dist/src/tools/read-file.test.js +98 -69
  128. package/dist/src/tools/read-file.test.js.map +1 -1
  129. package/dist/src/tools/read-many-files.d.ts +5 -2
  130. package/dist/src/tools/read-many-files.js +61 -16
  131. package/dist/src/tools/read-many-files.js.map +1 -1
  132. package/dist/src/tools/read-many-files.test.js +5 -2
  133. package/dist/src/tools/read-many-files.test.js.map +1 -1
  134. package/dist/src/tools/shell.d.ts +15 -26
  135. package/dist/src/tools/shell.js +137 -358
  136. package/dist/src/tools/shell.js.map +1 -1
  137. package/dist/src/tools/shell.test.js +73 -318
  138. package/dist/src/tools/shell.test.js.map +1 -1
  139. package/dist/src/tools/tool-registry.d.ts +13 -1
  140. package/dist/src/tools/tool-registry.js +45 -1
  141. package/dist/src/tools/tool-registry.js.map +1 -1
  142. package/dist/src/tools/tool-registry.test.js +3 -3
  143. package/dist/src/tools/tool-registry.test.js.map +1 -1
  144. package/dist/src/utils/bfsFileSearch.d.ts +2 -0
  145. package/dist/src/utils/bfsFileSearch.js +4 -1
  146. package/dist/src/utils/bfsFileSearch.js.map +1 -1
  147. package/dist/src/utils/bfsFileSearch.test.js +108 -105
  148. package/dist/src/utils/bfsFileSearch.test.js.map +1 -1
  149. package/dist/src/utils/editCorrector.js +4 -4
  150. package/dist/src/utils/editCorrector.js.map +1 -1
  151. package/dist/src/utils/editCorrector.test.js +1 -1
  152. package/dist/src/utils/editor.js +16 -10
  153. package/dist/src/utils/editor.js.map +1 -1
  154. package/dist/src/utils/editor.test.js +128 -28
  155. package/dist/src/utils/editor.test.js.map +1 -1
  156. package/dist/src/utils/errorReporting.d.ts +1 -1
  157. package/dist/src/utils/errorReporting.js +2 -2
  158. package/dist/src/utils/errorReporting.js.map +1 -1
  159. package/dist/src/utils/errorReporting.test.js +44 -38
  160. package/dist/src/utils/errorReporting.test.js.map +1 -1
  161. package/dist/src/utils/fileUtils.d.ts +4 -4
  162. package/dist/src/utils/fileUtils.js +31 -15
  163. package/dist/src/utils/fileUtils.js.map +1 -1
  164. package/dist/src/utils/fileUtils.test.js +37 -37
  165. package/dist/src/utils/fileUtils.test.js.map +1 -1
  166. package/dist/src/utils/getFolderStructure.d.ts +3 -2
  167. package/dist/src/utils/getFolderStructure.js +27 -28
  168. package/dist/src/utils/getFolderStructure.js.map +1 -1
  169. package/dist/src/utils/getFolderStructure.test.js +169 -185
  170. package/dist/src/utils/getFolderStructure.test.js.map +1 -1
  171. package/dist/src/utils/gitIgnoreParser.js +4 -7
  172. package/dist/src/utils/gitIgnoreParser.js.map +1 -1
  173. package/dist/src/utils/gitIgnoreParser.test.js +71 -62
  174. package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
  175. package/dist/src/utils/memoryDiscovery.d.ts +2 -1
  176. package/dist/src/utils/memoryDiscovery.js +11 -5
  177. package/dist/src/utils/memoryDiscovery.js.map +1 -1
  178. package/dist/src/utils/memoryDiscovery.test.js +160 -371
  179. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  180. package/dist/src/utils/partUtils.d.ts +14 -0
  181. package/dist/src/utils/partUtils.js +65 -0
  182. package/dist/src/utils/partUtils.js.map +1 -0
  183. package/dist/src/utils/partUtils.test.d.ts +6 -0
  184. package/dist/src/utils/partUtils.test.js +130 -0
  185. package/dist/src/utils/partUtils.test.js.map +1 -0
  186. package/dist/src/utils/paths.d.ts +11 -0
  187. package/dist/src/utils/paths.js +17 -1
  188. package/dist/src/utils/paths.js.map +1 -1
  189. package/dist/src/utils/quotaErrorDetection.js.map +1 -1
  190. package/dist/src/utils/retry.js +1 -1
  191. package/dist/src/utils/retry.js.map +1 -1
  192. package/dist/src/utils/schemaValidator.js +5 -2
  193. package/dist/src/utils/schemaValidator.js.map +1 -1
  194. package/dist/src/utils/shell-utils.d.ts +44 -0
  195. package/dist/src/utils/shell-utils.js +243 -0
  196. package/dist/src/utils/shell-utils.js.map +1 -0
  197. package/dist/src/utils/shell-utils.test.d.ts +6 -0
  198. package/dist/src/utils/shell-utils.test.js +450 -0
  199. package/dist/src/utils/shell-utils.test.js.map +1 -0
  200. package/dist/src/utils/summarizer.js +1 -30
  201. package/dist/src/utils/summarizer.js.map +1 -1
  202. package/dist/src/utils/systemEncoding.d.ts +40 -0
  203. package/dist/src/utils/systemEncoding.js +149 -0
  204. package/dist/src/utils/systemEncoding.js.map +1 -0
  205. package/dist/src/utils/systemEncoding.test.d.ts +6 -0
  206. package/dist/src/utils/systemEncoding.test.js +368 -0
  207. package/dist/src/utils/systemEncoding.test.js.map +1 -0
  208. package/dist/src/utils/user_account.test.js +1 -1
  209. package/dist/src/utils/user_account.test.js.map +1 -1
  210. package/dist/tsconfig.tsbuildinfo +1 -1
  211. package/package.json +4 -1
  212. package/dist/src/core/geminiRequest.test.js +0 -72
  213. package/dist/src/core/geminiRequest.test.js.map +0 -1
  214. package/dist/src/services/ideContext.d.ts +0 -126
  215. package/dist/src/services/ideContext.js.map +0 -1
  216. package/dist/src/services/ideContext.test.js.map +0 -1
  217. /package/dist/src/{services → ide}/ideContext.test.d.ts +0 -0
  218. /package/dist/src/{core/geminiRequest.test.d.ts → mcp/google-auth-provider.test.d.ts} +0 -0
@@ -3,430 +3,219 @@
3
3
  * Copyright 2025 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- import { vi, describe, it, expect, beforeEach } from 'vitest';
6
+ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
7
7
  import * as fsPromises from 'fs/promises';
8
8
  import * as os from 'os';
9
9
  import * as path from 'path';
10
10
  import { loadServerHierarchicalMemory } from './memoryDiscovery.js';
11
- import { LLXPRT_CONFIG_DIR, setLlxprtMdFilename, getCurrentLlxprtMdFilename, DEFAULT_CONTEXT_FILENAME, } from '../tools/memoryTool.js';
11
+ import { LLXPRT_CONFIG_DIR, setLlxprtMdFilename, DEFAULT_CONTEXT_FILENAME, } from '../tools/memoryTool.js';
12
12
  import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
13
- const ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST = DEFAULT_CONTEXT_FILENAME;
14
- // Mock the entire fs/promises module
15
- vi.mock('fs/promises');
16
- // Mock the parts of fsSync we might use (like constants or existsSync if needed)
17
- vi.mock('fs', async (importOriginal) => {
18
- const actual = await importOriginal();
13
+ vi.mock('os', async (importOriginal) => {
14
+ const actualOs = await importOriginal();
19
15
  return {
20
- ...actual, // Spread actual to get all exports, including Stats and Dirent if they are classes/constructors
21
- constants: { ...actual.constants }, // Preserve constants
16
+ ...actualOs,
17
+ homedir: vi.fn(),
22
18
  };
23
19
  });
24
- vi.mock('os');
25
20
  describe('loadServerHierarchicalMemory', () => {
26
- const mockFs = fsPromises;
27
- const mockOs = os;
28
- const CWD = '/test/project/src';
29
- const PROJECT_ROOT = '/test/project';
30
- const USER_HOME = '/test/userhome';
31
- let GLOBAL_LLXPRT_DIR;
32
- let GLOBAL_GEMINI_FILE; // Defined in beforeEach
33
- const fileService = new FileDiscoveryService(PROJECT_ROOT);
34
- beforeEach(() => {
21
+ let testRootDir;
22
+ let cwd;
23
+ let projectRoot;
24
+ let homedir;
25
+ async function createEmptyDir(fullPath) {
26
+ await fsPromises.mkdir(fullPath, { recursive: true });
27
+ return fullPath;
28
+ }
29
+ async function createTestFile(fullPath, fileContents) {
30
+ await fsPromises.mkdir(path.dirname(fullPath), { recursive: true });
31
+ await fsPromises.writeFile(fullPath, fileContents);
32
+ return path.resolve(testRootDir, fullPath);
33
+ }
34
+ beforeEach(async () => {
35
+ testRootDir = await fsPromises.mkdtemp(path.join(os.tmpdir(), 'folder-structure-test-'));
35
36
  vi.resetAllMocks();
36
37
  // Set environment variables to indicate test environment
37
38
  process.env.NODE_ENV = 'test';
38
39
  process.env.VITEST = 'true';
39
- setLlxprtMdFilename(DEFAULT_CONTEXT_FILENAME); // Use defined const
40
- mockOs.homedir.mockReturnValue(USER_HOME);
41
- // Define these here to use potentially reset/updated values from imports
42
- GLOBAL_LLXPRT_DIR = path.join(USER_HOME, LLXPRT_CONFIG_DIR);
43
- GLOBAL_GEMINI_FILE = path.join(GLOBAL_LLXPRT_DIR, getCurrentLlxprtMdFilename());
44
- mockFs.stat.mockRejectedValue(new Error('File not found'));
45
- mockFs.readdir.mockResolvedValue([]);
46
- mockFs.readFile.mockRejectedValue(new Error('File not found'));
47
- mockFs.access.mockRejectedValue(new Error('File not found'));
40
+ projectRoot = await createEmptyDir(path.join(testRootDir, 'project'));
41
+ cwd = await createEmptyDir(path.join(projectRoot, 'src'));
42
+ homedir = await createEmptyDir(path.join(testRootDir, 'userhome'));
43
+ vi.mocked(os.homedir).mockReturnValue(homedir);
44
+ });
45
+ afterEach(async () => {
46
+ // Some tests set this to a different value.
47
+ setLlxprtMdFilename(DEFAULT_CONTEXT_FILENAME);
48
+ // Clean up the temporary directory to prevent resource leaks.
49
+ await fsPromises.rm(testRootDir, { recursive: true, force: true });
48
50
  });
49
51
  it('should return empty memory and count if no context files are found', async () => {
50
- const { memoryContent, fileCount } = await loadServerHierarchicalMemory(CWD, false, fileService);
51
- expect(memoryContent).toBe('');
52
- expect(fileCount).toBe(0);
52
+ const result = await loadServerHierarchicalMemory(cwd, false, new FileDiscoveryService(projectRoot));
53
+ expect(result).toEqual({
54
+ memoryContent: '',
55
+ fileCount: 0,
56
+ });
53
57
  });
54
58
  it('should load only the global context file if present and others are not (default filename)', async () => {
55
- const globalDefaultFile = path.join(GLOBAL_LLXPRT_DIR, DEFAULT_CONTEXT_FILENAME);
56
- mockFs.access.mockImplementation(async (p) => {
57
- if (p === globalDefaultFile) {
58
- return undefined;
59
- }
60
- throw new Error('File not found');
59
+ const defaultContextFile = await createTestFile(path.join(homedir, LLXPRT_CONFIG_DIR, DEFAULT_CONTEXT_FILENAME), 'default context content');
60
+ const result = await loadServerHierarchicalMemory(cwd, false, new FileDiscoveryService(projectRoot));
61
+ expect(result).toEqual({
62
+ memoryContent: `--- Context from: ${path.relative(cwd, defaultContextFile)} ---
63
+ default context content
64
+ --- End of Context from: ${path.relative(cwd, defaultContextFile)} ---`,
65
+ fileCount: 1,
61
66
  });
62
- mockFs.readFile.mockImplementation(async (p) => {
63
- if (p === globalDefaultFile) {
64
- return 'Global memory content';
65
- }
66
- throw new Error('File not found');
67
- });
68
- const { memoryContent, fileCount } = await loadServerHierarchicalMemory(CWD, false, fileService);
69
- expect(memoryContent).toBe(`--- Context from: ${path.relative(CWD, globalDefaultFile)} ---\nGlobal memory content\n--- End of Context from: ${path.relative(CWD, globalDefaultFile)} ---`);
70
- expect(fileCount).toBe(1);
71
- expect(mockFs.readFile).toHaveBeenCalledWith(globalDefaultFile, 'utf-8');
72
67
  });
73
68
  it('should load only the global custom context file if present and filename is changed', async () => {
74
69
  const customFilename = 'CUSTOM_AGENTS.md';
75
70
  setLlxprtMdFilename(customFilename);
76
- const globalCustomFile = path.join(GLOBAL_LLXPRT_DIR, customFilename);
77
- mockFs.access.mockImplementation(async (p) => {
78
- if (p === globalCustomFile) {
79
- return undefined;
80
- }
81
- throw new Error('File not found');
82
- });
83
- mockFs.readFile.mockImplementation(async (p) => {
84
- if (p === globalCustomFile) {
85
- return 'Global custom memory';
86
- }
87
- throw new Error('File not found');
71
+ const customContextFile = await createTestFile(path.join(homedir, LLXPRT_CONFIG_DIR, customFilename), 'custom context content');
72
+ const result = await loadServerHierarchicalMemory(cwd, false, new FileDiscoveryService(projectRoot));
73
+ expect(result).toEqual({
74
+ memoryContent: `--- Context from: ${path.relative(cwd, customContextFile)} ---
75
+ custom context content
76
+ --- End of Context from: ${path.relative(cwd, customContextFile)} ---`,
77
+ fileCount: 1,
88
78
  });
89
- const { memoryContent, fileCount } = await loadServerHierarchicalMemory(CWD, false, fileService);
90
- expect(memoryContent).toBe(`--- Context from: ${path.relative(CWD, globalCustomFile)} ---\nGlobal custom memory\n--- End of Context from: ${path.relative(CWD, globalCustomFile)} ---`);
91
- expect(fileCount).toBe(1);
92
- expect(mockFs.readFile).toHaveBeenCalledWith(globalCustomFile, 'utf-8');
93
79
  });
94
80
  it('should load context files by upward traversal with custom filename', async () => {
95
81
  const customFilename = 'PROJECT_CONTEXT.md';
96
82
  setLlxprtMdFilename(customFilename);
97
- const projectRootCustomFile = path.join(PROJECT_ROOT, customFilename);
98
- const srcCustomFile = path.join(CWD, customFilename);
99
- mockFs.stat.mockImplementation(async (p) => {
100
- if (p === path.join(PROJECT_ROOT, '.git')) {
101
- return { isDirectory: () => true };
102
- }
103
- throw new Error('File not found');
83
+ const projectContextFile = await createTestFile(path.join(projectRoot, customFilename), 'project context content');
84
+ const cwdContextFile = await createTestFile(path.join(cwd, customFilename), 'cwd context content');
85
+ const result = await loadServerHierarchicalMemory(cwd, false, new FileDiscoveryService(projectRoot));
86
+ expect(result).toEqual({
87
+ memoryContent: `--- Context from: ${path.relative(cwd, projectContextFile)} ---
88
+ project context content
89
+ --- End of Context from: ${path.relative(cwd, projectContextFile)} ---
90
+
91
+ --- Context from: ${path.relative(cwd, cwdContextFile)} ---
92
+ cwd context content
93
+ --- End of Context from: ${path.relative(cwd, cwdContextFile)} ---`,
94
+ fileCount: 2,
104
95
  });
105
- mockFs.access.mockImplementation(async (p) => {
106
- if (p === projectRootCustomFile || p === srcCustomFile) {
107
- return undefined;
108
- }
109
- throw new Error('File not found');
110
- });
111
- mockFs.readFile.mockImplementation(async (p) => {
112
- if (p === projectRootCustomFile) {
113
- return 'Project root custom memory';
114
- }
115
- if (p === srcCustomFile) {
116
- return 'Src directory custom memory';
117
- }
118
- throw new Error('File not found');
119
- });
120
- const { memoryContent, fileCount } = await loadServerHierarchicalMemory(CWD, false, fileService);
121
- const expectedContent = `--- Context from: ${path.relative(CWD, projectRootCustomFile)} ---\nProject root custom memory\n--- End of Context from: ${path.relative(CWD, projectRootCustomFile)} ---\n\n` +
122
- `--- Context from: ${customFilename} ---\nSrc directory custom memory\n--- End of Context from: ${customFilename} ---`;
123
- expect(memoryContent).toBe(expectedContent);
124
- expect(fileCount).toBe(2);
125
- expect(mockFs.readFile).toHaveBeenCalledWith(projectRootCustomFile, 'utf-8');
126
- expect(mockFs.readFile).toHaveBeenCalledWith(srcCustomFile, 'utf-8');
127
96
  });
128
97
  it('should load context files by downward traversal with custom filename', async () => {
129
98
  const customFilename = 'LOCAL_CONTEXT.md';
130
99
  setLlxprtMdFilename(customFilename);
131
- const subDir = path.join(CWD, 'subdir');
132
- const subDirCustomFile = path.join(subDir, customFilename);
133
- const cwdCustomFile = path.join(CWD, customFilename);
134
- mockFs.access.mockImplementation(async (p) => {
135
- if (p === cwdCustomFile || p === subDirCustomFile)
136
- return undefined;
137
- throw new Error('File not found');
100
+ await createTestFile(path.join(cwd, 'subdir', customFilename), 'Subdir custom memory');
101
+ await createTestFile(path.join(cwd, customFilename), 'CWD custom memory');
102
+ const result = await loadServerHierarchicalMemory(cwd, false, new FileDiscoveryService(projectRoot));
103
+ expect(result).toEqual({
104
+ memoryContent: `--- Context from: ${customFilename} ---
105
+ CWD custom memory
106
+ --- End of Context from: ${customFilename} ---
107
+
108
+ --- Context from: ${path.join('subdir', customFilename)} ---
109
+ Subdir custom memory
110
+ --- End of Context from: ${path.join('subdir', customFilename)} ---`,
111
+ fileCount: 2,
138
112
  });
139
- mockFs.readFile.mockImplementation(async (p) => {
140
- if (p === cwdCustomFile)
141
- return 'CWD custom memory';
142
- if (p === subDirCustomFile)
143
- return 'Subdir custom memory';
144
- throw new Error('File not found');
145
- });
146
- mockFs.readdir.mockImplementation((async (p) => {
147
- if (p === CWD) {
148
- return [
149
- {
150
- name: customFilename,
151
- isFile: () => true,
152
- isDirectory: () => false,
153
- },
154
- {
155
- name: 'subdir',
156
- isFile: () => false,
157
- isDirectory: () => true,
158
- },
159
- ];
160
- }
161
- if (p === subDir) {
162
- return [
163
- {
164
- name: customFilename,
165
- isFile: () => true,
166
- isDirectory: () => false,
167
- },
168
- ];
169
- }
170
- return [];
171
- }));
172
- const { memoryContent, fileCount } = await loadServerHierarchicalMemory(CWD, false, fileService);
173
- const expectedContent = `--- Context from: ${customFilename} ---\nCWD custom memory\n--- End of Context from: ${customFilename} ---\n\n` +
174
- `--- Context from: ${path.join('subdir', customFilename)} ---\nSubdir custom memory\n--- End of Context from: ${path.join('subdir', customFilename)} ---`;
175
- expect(memoryContent).toBe(expectedContent);
176
- expect(fileCount).toBe(2);
177
113
  });
178
114
  it('should load ORIGINAL_GEMINI_MD_FILENAME files by upward traversal from CWD to project root', async () => {
179
- const projectRootGeminiFile = path.join(PROJECT_ROOT, ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST);
180
- const srcGeminiFile = path.join(CWD, ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST);
181
- mockFs.stat.mockImplementation(async (p) => {
182
- if (p === path.join(PROJECT_ROOT, '.git')) {
183
- return { isDirectory: () => true };
184
- }
185
- throw new Error('File not found');
186
- });
187
- mockFs.access.mockImplementation(async (p) => {
188
- if (p === projectRootGeminiFile || p === srcGeminiFile) {
189
- return undefined;
190
- }
191
- throw new Error('File not found');
115
+ const projectRootGeminiFile = await createTestFile(path.join(projectRoot, DEFAULT_CONTEXT_FILENAME), 'Project root memory');
116
+ const srcGeminiFile = await createTestFile(path.join(cwd, DEFAULT_CONTEXT_FILENAME), 'Src directory memory');
117
+ const result = await loadServerHierarchicalMemory(cwd, false, new FileDiscoveryService(projectRoot));
118
+ expect(result).toEqual({
119
+ memoryContent: `--- Context from: ${path.relative(cwd, projectRootGeminiFile)} ---
120
+ Project root memory
121
+ --- End of Context from: ${path.relative(cwd, projectRootGeminiFile)} ---
122
+
123
+ --- Context from: ${path.relative(cwd, srcGeminiFile)} ---
124
+ Src directory memory
125
+ --- End of Context from: ${path.relative(cwd, srcGeminiFile)} ---`,
126
+ fileCount: 2,
192
127
  });
193
- mockFs.readFile.mockImplementation(async (p) => {
194
- if (p === projectRootGeminiFile) {
195
- return 'Project root memory';
196
- }
197
- if (p === srcGeminiFile) {
198
- return 'Src directory memory';
199
- }
200
- throw new Error('File not found');
201
- });
202
- const { memoryContent, fileCount } = await loadServerHierarchicalMemory(CWD, false, fileService);
203
- const expectedContent = `--- Context from: ${path.relative(CWD, projectRootGeminiFile)} ---\nProject root memory\n--- End of Context from: ${path.relative(CWD, projectRootGeminiFile)} ---\n\n` +
204
- `--- Context from: ${ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST} ---\nSrc directory memory\n--- End of Context from: ${ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST} ---`;
205
- expect(memoryContent).toBe(expectedContent);
206
- expect(fileCount).toBe(2);
207
- expect(mockFs.readFile).toHaveBeenCalledWith(projectRootGeminiFile, 'utf-8');
208
- expect(mockFs.readFile).toHaveBeenCalledWith(srcGeminiFile, 'utf-8');
209
128
  });
210
129
  it('should load ORIGINAL_GEMINI_MD_FILENAME files by downward traversal from CWD', async () => {
211
- const subDir = path.join(CWD, 'subdir');
212
- const subDirGeminiFile = path.join(subDir, ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST);
213
- const cwdGeminiFile = path.join(CWD, ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST);
214
- mockFs.access.mockImplementation(async (p) => {
215
- if (p === cwdGeminiFile || p === subDirGeminiFile)
216
- return undefined;
217
- throw new Error('File not found');
218
- });
219
- mockFs.readFile.mockImplementation(async (p) => {
220
- if (p === cwdGeminiFile)
221
- return 'CWD memory';
222
- if (p === subDirGeminiFile)
223
- return 'Subdir memory';
224
- throw new Error('File not found');
130
+ await createTestFile(path.join(cwd, 'subdir', DEFAULT_CONTEXT_FILENAME), 'Subdir memory');
131
+ await createTestFile(path.join(cwd, DEFAULT_CONTEXT_FILENAME), 'CWD memory');
132
+ const result = await loadServerHierarchicalMemory(cwd, false, new FileDiscoveryService(projectRoot));
133
+ expect(result).toEqual({
134
+ memoryContent: `--- Context from: ${DEFAULT_CONTEXT_FILENAME} ---
135
+ CWD memory
136
+ --- End of Context from: ${DEFAULT_CONTEXT_FILENAME} ---
137
+
138
+ --- Context from: ${path.join('subdir', DEFAULT_CONTEXT_FILENAME)} ---
139
+ Subdir memory
140
+ --- End of Context from: ${path.join('subdir', DEFAULT_CONTEXT_FILENAME)} ---`,
141
+ fileCount: 2,
225
142
  });
226
- mockFs.readdir.mockImplementation((async (p) => {
227
- if (p === CWD) {
228
- return [
229
- {
230
- name: ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST,
231
- isFile: () => true,
232
- isDirectory: () => false,
233
- },
234
- {
235
- name: 'subdir',
236
- isFile: () => false,
237
- isDirectory: () => true,
238
- },
239
- ];
240
- }
241
- if (p === subDir) {
242
- return [
243
- {
244
- name: ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST,
245
- isFile: () => true,
246
- isDirectory: () => false,
247
- },
248
- ];
249
- }
250
- return [];
251
- }));
252
- const { memoryContent, fileCount } = await loadServerHierarchicalMemory(CWD, false, fileService);
253
- const expectedContent = `--- Context from: ${ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST} ---\nCWD memory\n--- End of Context from: ${ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST} ---\n\n` +
254
- `--- Context from: ${path.join('subdir', ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST)} ---\nSubdir memory\n--- End of Context from: ${path.join('subdir', ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST)} ---`;
255
- expect(memoryContent).toBe(expectedContent);
256
- expect(fileCount).toBe(2);
257
143
  });
258
144
  it('should load and correctly order global, upward, and downward ORIGINAL_GEMINI_MD_FILENAME files', async () => {
259
- setLlxprtMdFilename(ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST); // Explicitly set for this test
260
- const globalFileToUse = path.join(GLOBAL_LLXPRT_DIR, ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST);
261
- const projectParentDir = path.dirname(PROJECT_ROOT);
262
- const projectParentGeminiFile = path.join(projectParentDir, ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST);
263
- const projectRootGeminiFile = path.join(PROJECT_ROOT, ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST);
264
- const cwdGeminiFile = path.join(CWD, ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST);
265
- const subDir = path.join(CWD, 'sub');
266
- const subDirGeminiFile = path.join(subDir, ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST);
267
- mockFs.stat.mockImplementation(async (p) => {
268
- if (p === path.join(PROJECT_ROOT, '.git')) {
269
- return { isDirectory: () => true };
270
- }
271
- else if (p === path.join(PROJECT_ROOT, '.llxprt')) {
272
- return { isDirectory: () => true };
273
- }
274
- throw new Error('File not found');
275
- });
276
- mockFs.access.mockImplementation(async (p) => {
277
- if (p === globalFileToUse || // Use the dynamically set global file path
278
- p === projectParentGeminiFile ||
279
- p === projectRootGeminiFile ||
280
- p === cwdGeminiFile ||
281
- p === subDirGeminiFile) {
282
- return undefined;
283
- }
284
- throw new Error('File not found');
285
- });
286
- mockFs.readFile.mockImplementation(async (p) => {
287
- if (p === globalFileToUse)
288
- return 'Global memory'; // Use the dynamically set global file path
289
- if (p === projectParentGeminiFile)
290
- return 'Project parent memory';
291
- if (p === projectRootGeminiFile)
292
- return 'Project root memory';
293
- if (p === cwdGeminiFile)
294
- return 'CWD memory';
295
- if (p === subDirGeminiFile)
296
- return 'Subdir memory';
297
- throw new Error('File not found');
145
+ const defaultContextFile = await createTestFile(path.join(homedir, LLXPRT_CONFIG_DIR, DEFAULT_CONTEXT_FILENAME), 'default context content');
146
+ const rootGeminiFile = await createTestFile(path.join(testRootDir, DEFAULT_CONTEXT_FILENAME), 'Project parent memory');
147
+ const projectRootGeminiFile = await createTestFile(path.join(projectRoot, DEFAULT_CONTEXT_FILENAME), 'Project root memory');
148
+ const cwdGeminiFile = await createTestFile(path.join(cwd, DEFAULT_CONTEXT_FILENAME), 'CWD memory');
149
+ const subDirGeminiFile = await createTestFile(path.join(cwd, 'sub', DEFAULT_CONTEXT_FILENAME), 'Subdir memory');
150
+ const result = await loadServerHierarchicalMemory(cwd, false, new FileDiscoveryService(projectRoot));
151
+ expect(result).toEqual({
152
+ memoryContent: `--- Context from: ${path.relative(cwd, defaultContextFile)} ---
153
+ default context content
154
+ --- End of Context from: ${path.relative(cwd, defaultContextFile)} ---
155
+
156
+ --- Context from: ${path.relative(cwd, rootGeminiFile)} ---
157
+ Project parent memory
158
+ --- End of Context from: ${path.relative(cwd, rootGeminiFile)} ---
159
+
160
+ --- Context from: ${path.relative(cwd, projectRootGeminiFile)} ---
161
+ Project root memory
162
+ --- End of Context from: ${path.relative(cwd, projectRootGeminiFile)} ---
163
+
164
+ --- Context from: ${path.relative(cwd, cwdGeminiFile)} ---
165
+ CWD memory
166
+ --- End of Context from: ${path.relative(cwd, cwdGeminiFile)} ---
167
+
168
+ --- Context from: ${path.relative(cwd, subDirGeminiFile)} ---
169
+ Subdir memory
170
+ --- End of Context from: ${path.relative(cwd, subDirGeminiFile)} ---`,
171
+ fileCount: 5,
298
172
  });
299
- mockFs.readdir.mockImplementation((async (p) => {
300
- if (p === CWD) {
301
- return [
302
- {
303
- name: 'sub',
304
- isFile: () => false,
305
- isDirectory: () => true,
306
- },
307
- ];
308
- }
309
- if (p === subDir) {
310
- return [
311
- {
312
- name: ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST,
313
- isFile: () => true,
314
- isDirectory: () => false,
315
- },
316
- ];
317
- }
318
- return [];
319
- }));
320
- const { memoryContent, fileCount } = await loadServerHierarchicalMemory(CWD, false, fileService);
321
- const relPathGlobal = path.relative(CWD, GLOBAL_GEMINI_FILE);
322
- const relPathProjectParent = path.relative(CWD, projectParentGeminiFile);
323
- const relPathProjectRoot = path.relative(CWD, projectRootGeminiFile);
324
- const relPathCwd = ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST;
325
- const relPathSubDir = path.join('sub', ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST);
326
- const expectedContent = [
327
- `--- Context from: ${relPathGlobal} ---\nGlobal memory\n--- End of Context from: ${relPathGlobal} ---`,
328
- `--- Context from: ${relPathProjectParent} ---\nProject parent memory\n--- End of Context from: ${relPathProjectParent} ---`,
329
- `--- Context from: ${relPathProjectRoot} ---\nProject root memory\n--- End of Context from: ${relPathProjectRoot} ---`,
330
- `--- Context from: ${relPathCwd} ---\nCWD memory\n--- End of Context from: ${relPathCwd} ---`,
331
- `--- Context from: ${relPathSubDir} ---\nSubdir memory\n--- End of Context from: ${relPathSubDir} ---`,
332
- ].join('\n\n');
333
- expect(memoryContent).toBe(expectedContent);
334
- expect(fileCount).toBe(5);
335
173
  });
336
174
  it('should ignore specified directories during downward scan', async () => {
337
- const ignoredDir = path.join(CWD, 'node_modules');
338
- const ignoredDirGeminiFile = path.join(ignoredDir, ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST); // Corrected
339
- const regularSubDir = path.join(CWD, 'my_code');
340
- const regularSubDirGeminiFile = path.join(regularSubDir, ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST);
341
- mockFs.access.mockImplementation(async (p) => {
342
- if (p === regularSubDirGeminiFile)
343
- return undefined;
344
- if (p === ignoredDirGeminiFile)
345
- throw new Error('Should not access ignored file');
346
- throw new Error('File not found');
175
+ await createEmptyDir(path.join(projectRoot, '.git'));
176
+ await createTestFile(path.join(projectRoot, '.gitignore'), 'node_modules');
177
+ await createTestFile(path.join(cwd, 'node_modules', DEFAULT_CONTEXT_FILENAME), 'Ignored memory');
178
+ const regularSubDirGeminiFile = await createTestFile(path.join(cwd, 'my_code', DEFAULT_CONTEXT_FILENAME), 'My code memory');
179
+ const result = await loadServerHierarchicalMemory(cwd, false, new FileDiscoveryService(projectRoot), [], {
180
+ respectGitIgnore: true,
181
+ respectLlxprtIgnore: true,
182
+ });
183
+ expect(result).toEqual({
184
+ memoryContent: `--- Context from: ${path.relative(cwd, regularSubDirGeminiFile)} ---
185
+ My code memory
186
+ --- End of Context from: ${path.relative(cwd, regularSubDirGeminiFile)} ---`,
187
+ fileCount: 1,
347
188
  });
348
- mockFs.readFile.mockImplementation(async (p) => {
349
- if (p === regularSubDirGeminiFile)
350
- return 'My code memory';
351
- throw new Error('File not found');
352
- });
353
- mockFs.readdir.mockImplementation((async (p) => {
354
- if (p === CWD) {
355
- return [
356
- {
357
- name: 'node_modules',
358
- isFile: () => false,
359
- isDirectory: () => true,
360
- },
361
- {
362
- name: 'my_code',
363
- isFile: () => false,
364
- isDirectory: () => true,
365
- },
366
- ];
367
- }
368
- if (p === regularSubDir) {
369
- return [
370
- {
371
- name: ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST,
372
- isFile: () => true,
373
- isDirectory: () => false,
374
- },
375
- ];
376
- }
377
- if (p === ignoredDir) {
378
- return [];
379
- }
380
- return [];
381
- }));
382
- const { memoryContent, fileCount } = await loadServerHierarchicalMemory(CWD, false, fileService);
383
- const expectedContent = `--- Context from: ${path.join('my_code', ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST)} ---\nMy code memory\n--- End of Context from: ${path.join('my_code', ORIGINAL_LLXPRT_MD_FILENAME_CONST_FOR_TEST)} ---`;
384
- expect(memoryContent).toBe(expectedContent);
385
- expect(fileCount).toBe(1);
386
- expect(mockFs.readFile).not.toHaveBeenCalledWith(ignoredDirGeminiFile, 'utf-8');
387
189
  });
388
- it('should respect MAX_DIRECTORIES_TO_SCAN_FOR_MEMORY during downward scan', async () => {
190
+ it('should respect the maxDirs parameter during downward scan', async () => {
389
191
  const consoleDebugSpy = vi
390
192
  .spyOn(console, 'debug')
391
193
  .mockImplementation(() => { });
392
- const dirNames = [];
393
- for (let i = 0; i < 250; i++) {
394
- dirNames.push({
395
- name: `deep_dir_${i}`,
396
- isFile: () => false,
397
- isDirectory: () => true,
398
- });
194
+ for (let i = 0; i < 100; i++) {
195
+ await createEmptyDir(path.join(cwd, `deep_dir_${i}`));
399
196
  }
400
- mockFs.readdir.mockImplementation((async (p) => {
401
- if (p === CWD)
402
- return dirNames;
403
- if (p.toString().startsWith(path.join(CWD, 'deep_dir_')))
404
- return [];
405
- return [];
406
- }));
407
- mockFs.access.mockRejectedValue(new Error('not found'));
408
- await loadServerHierarchicalMemory(CWD, true, fileService);
409
- expect(consoleDebugSpy).toHaveBeenCalledWith(expect.stringContaining('[DEBUG] [BfsFileSearch]'), expect.stringContaining('Scanning [200/200]:'));
410
- consoleDebugSpy.mockRestore();
197
+ // Pass the custom limit directly to the function
198
+ await loadServerHierarchicalMemory(cwd, true, new FileDiscoveryService(projectRoot), [], {
199
+ respectGitIgnore: true,
200
+ respectLlxprtIgnore: true,
201
+ }, 50);
202
+ expect(consoleDebugSpy).toHaveBeenCalledWith(expect.stringContaining('[DEBUG] [BfsFileSearch]'), expect.stringContaining('Scanning [50/50]:'));
203
+ vi.mocked(console.debug).mockRestore();
204
+ const result = await loadServerHierarchicalMemory(cwd, false, new FileDiscoveryService(projectRoot));
205
+ expect(result).toEqual({
206
+ memoryContent: '',
207
+ fileCount: 0,
208
+ });
411
209
  });
412
210
  it('should load extension context file paths', async () => {
413
- const extensionFilePath = '/test/extensions/ext1/LLXPRT.md';
414
- mockFs.access.mockImplementation(async (p) => {
415
- if (p === extensionFilePath) {
416
- return undefined;
417
- }
418
- throw new Error('File not found');
419
- });
420
- mockFs.readFile.mockImplementation(async (p) => {
421
- if (p === extensionFilePath) {
422
- return 'Extension memory content';
423
- }
424
- throw new Error('File not found');
211
+ const extensionFilePath = await createTestFile(path.join(testRootDir, 'extensions/ext1/LLXPRT.md'), 'Extension memory content');
212
+ const result = await loadServerHierarchicalMemory(cwd, false, new FileDiscoveryService(projectRoot), [extensionFilePath]);
213
+ expect(result).toEqual({
214
+ memoryContent: `--- Context from: ${path.relative(cwd, extensionFilePath)} ---
215
+ Extension memory content
216
+ --- End of Context from: ${path.relative(cwd, extensionFilePath)} ---`,
217
+ fileCount: 1,
425
218
  });
426
- const { memoryContent, fileCount } = await loadServerHierarchicalMemory(CWD, false, fileService, [extensionFilePath]);
427
- expect(memoryContent).toBe(`--- Context from: ${path.relative(CWD, extensionFilePath)} ---\nExtension memory content\n--- End of Context from: ${path.relative(CWD, extensionFilePath)} ---`);
428
- expect(fileCount).toBe(1);
429
- expect(mockFs.readFile).toHaveBeenCalledWith(extensionFilePath, 'utf-8');
430
219
  });
431
220
  });
432
221
  //# sourceMappingURL=memoryDiscovery.test.js.map