@vybestack/llxprt-code 0.8.0-nightly.260112.d9cf7fbc6 → 0.8.0-nightly.260113.48db4b09b

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 (65) hide show
  1. package/dist/package.json +3 -3
  2. package/dist/src/generated/git-commit.d.ts +1 -1
  3. package/dist/src/generated/git-commit.js +1 -1
  4. package/dist/src/runtime/runtimeSettings.d.ts +1 -0
  5. package/dist/src/runtime/runtimeSettings.js +4 -0
  6. package/dist/src/runtime/runtimeSettings.js.map +1 -1
  7. package/dist/src/ui/AppContainer.js +43 -0
  8. package/dist/src/ui/AppContainer.js.map +1 -1
  9. package/dist/src/ui/commands/profileCommand.js +111 -9
  10. package/dist/src/ui/commands/profileCommand.js.map +1 -1
  11. package/dist/src/ui/commands/profileCommand.test.js +3 -6
  12. package/dist/src/ui/commands/profileCommand.test.js.map +1 -1
  13. package/dist/src/ui/commands/types.d.ts +12 -2
  14. package/dist/src/ui/commands/types.js.map +1 -1
  15. package/dist/src/ui/components/DialogManager.js +12 -0
  16. package/dist/src/ui/components/DialogManager.js.map +1 -1
  17. package/dist/src/ui/components/ProfileDetailDialog.d.ts +22 -0
  18. package/dist/src/ui/components/ProfileDetailDialog.js +113 -0
  19. package/dist/src/ui/components/ProfileDetailDialog.js.map +1 -0
  20. package/dist/src/ui/components/ProfileInlineEditor.d.ts +16 -0
  21. package/dist/src/ui/components/ProfileInlineEditor.js +216 -0
  22. package/dist/src/ui/components/ProfileInlineEditor.js.map +1 -0
  23. package/dist/src/ui/components/ProfileListDialog.d.ts +26 -0
  24. package/dist/src/ui/components/ProfileListDialog.js +172 -0
  25. package/dist/src/ui/components/ProfileListDialog.js.map +1 -0
  26. package/dist/src/ui/components/ProviderDialog.js +1 -1
  27. package/dist/src/ui/components/ProviderDialog.js.map +1 -1
  28. package/dist/src/ui/components/ProviderModelDialog.js +1 -1
  29. package/dist/src/ui/components/ProviderModelDialog.js.map +1 -1
  30. package/dist/src/ui/contexts/RuntimeContext.d.ts +2 -1
  31. package/dist/src/ui/contexts/RuntimeContext.js +2 -1
  32. package/dist/src/ui/contexts/RuntimeContext.js.map +1 -1
  33. package/dist/src/ui/contexts/UIActionsContext.d.ts +10 -0
  34. package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
  35. package/dist/src/ui/contexts/UIStateContext.d.ts +17 -0
  36. package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
  37. package/dist/src/ui/hooks/atCommandProcessor.js +25 -1
  38. package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
  39. package/dist/src/ui/hooks/atCommandProcessor.test.js +127 -91
  40. package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
  41. package/dist/src/ui/hooks/slashCommandProcessor.d.ts +3 -0
  42. package/dist/src/ui/hooks/slashCommandProcessor.js +28 -0
  43. package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
  44. package/dist/src/ui/hooks/useEditorSettings.test.js +3 -0
  45. package/dist/src/ui/hooks/useEditorSettings.test.js.map +1 -1
  46. package/dist/src/ui/hooks/useFolderTrust.js +1 -1
  47. package/dist/src/ui/hooks/useFolderTrust.js.map +1 -1
  48. package/dist/src/ui/hooks/useProfileManagement.d.ts +40 -0
  49. package/dist/src/ui/hooks/useProfileManagement.js +350 -0
  50. package/dist/src/ui/hooks/useProfileManagement.js.map +1 -0
  51. package/dist/src/ui/hooks/useReactToolScheduler.js +103 -33
  52. package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
  53. package/dist/src/ui/hooks/useSlashCompletion.test.js +1 -1
  54. package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
  55. package/dist/src/ui/hooks/useToolScheduler.test.js +322 -220
  56. package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  57. package/dist/src/ui/layouts/DefaultAppLayout.js +3 -0
  58. package/dist/src/ui/layouts/DefaultAppLayout.js.map +1 -1
  59. package/dist/src/ui/reducers/appReducer.d.ts +5 -2
  60. package/dist/src/ui/reducers/appReducer.js +3 -0
  61. package/dist/src/ui/reducers/appReducer.js.map +1 -1
  62. package/dist/src/ui/reducers/appReducer.test.js +6 -0
  63. package/dist/src/ui/reducers/appReducer.test.js.map +1 -1
  64. package/dist/tsconfig.build.tsbuildinfo +1 -1
  65. package/package.json +3 -3
@@ -9,6 +9,7 @@ import { FileDiscoveryService, GlobTool, ReadManyFilesTool, StandardFileSystemSe
9
9
  import * as os from 'os';
10
10
  import { ToolCallStatus } from '../types.js';
11
11
  import * as fsPromises from 'fs/promises';
12
+ import * as fs from 'fs';
12
13
  import * as path from 'path';
13
14
  // No mocking - use the real FileDiscoveryService
14
15
  describe('handleAtCommand', () => {
@@ -20,17 +21,29 @@ describe('handleAtCommand', () => {
20
21
  async function createTestFile(fullPath, fileContents) {
21
22
  await fsPromises.mkdir(path.dirname(fullPath), { recursive: true });
22
23
  await fsPromises.writeFile(fullPath, fileContents);
23
- return path.resolve(testRootDir, fullPath);
24
+ return fs.realpathSync(fullPath);
24
25
  }
26
+ let originalCwd;
25
27
  beforeEach(async () => {
26
28
  vi.resetAllMocks();
27
29
  testRootDir = await fsPromises.mkdtemp(path.join(os.tmpdir(), 'folder-structure-test-'));
30
+ originalCwd = process.cwd();
31
+ process.chdir(testRootDir);
28
32
  abortController = new AbortController();
29
33
  const getToolRegistry = vi.fn();
30
34
  mockConfig = {
31
35
  getToolRegistry,
32
36
  getTargetDir: () => testRootDir,
33
37
  isSandboxed: () => false,
38
+ getMessageBus: vi.fn(() => ({
39
+ subscribe: vi.fn(),
40
+ unsubscribe: vi.fn(),
41
+ publish: vi.fn(),
42
+ respondToConfirmation: vi.fn(),
43
+ requestConfirmation: vi.fn().mockResolvedValue(true),
44
+ removeAllListeners: vi.fn(),
45
+ listenerCount: vi.fn().mockReturnValue(0),
46
+ })),
34
47
  getFileService: () => new FileDiscoveryService(testRootDir),
35
48
  getFileFilteringRespectGitIgnore: () => true,
36
49
  getFileFilteringRespectLlxprtIgnore: () => true,
@@ -40,10 +53,31 @@ describe('handleAtCommand', () => {
40
53
  }),
41
54
  getFileSystemService: () => new StandardFileSystemService(),
42
55
  getEnableRecursiveFileSearch: vi.fn(() => true),
43
- getWorkspaceContext: () => ({
44
- isPathWithinWorkspace: () => true,
45
- getDirectories: () => [testRootDir],
46
- }),
56
+ getWorkspaceContext: () => {
57
+ const workspaceRoot = fs.realpathSync(testRootDir);
58
+ return {
59
+ isPathWithinWorkspace: (inputPath) => {
60
+ const absoluteInput = path.isAbsolute(inputPath)
61
+ ? inputPath
62
+ : path.resolve(testRootDir, inputPath);
63
+ let resolved;
64
+ try {
65
+ resolved = fs.realpathSync(absoluteInput);
66
+ }
67
+ catch {
68
+ if (absoluteInput.startsWith(testRootDir)) {
69
+ resolved = path.resolve(workspaceRoot, path.relative(testRootDir, absoluteInput));
70
+ }
71
+ else {
72
+ resolved = path.normalize(absoluteInput);
73
+ }
74
+ }
75
+ return (resolved === workspaceRoot ||
76
+ resolved.startsWith(workspaceRoot + path.sep));
77
+ },
78
+ getDirectories: () => [workspaceRoot],
79
+ };
80
+ },
47
81
  getEphemeralSettings: () => ({}), // No disabled tools
48
82
  getMcpServers: () => ({}),
49
83
  getMcpServerCommand: () => undefined,
@@ -67,6 +101,7 @@ describe('handleAtCommand', () => {
67
101
  });
68
102
  afterEach(async () => {
69
103
  abortController.abort();
104
+ process.chdir(originalCwd);
70
105
  await fsPromises.rm(testRootDir, { recursive: true, force: true });
71
106
  });
72
107
  it('should pass through query if no @ command is present', async () => {
@@ -110,7 +145,7 @@ describe('handleAtCommand', () => {
110
145
  const fileContent = 'This is the file content.';
111
146
  // Create file in the test directory
112
147
  const relativePath = path.join('path', 'to', 'file.txt');
113
- const filePath = await createTestFile(path.join(testRootDir, relativePath), fileContent);
148
+ await createTestFile(path.join(testRootDir, relativePath), fileContent);
114
149
  // Use relative path in the query
115
150
  const query = `@${relativePath}`;
116
151
  const result = await handleAtCommand({
@@ -125,7 +160,7 @@ describe('handleAtCommand', () => {
125
160
  processedQuery: [
126
161
  { text: `@${relativePath}` },
127
162
  { text: '\n--- Content from referenced files ---' },
128
- { text: `\nContent from @${filePath}:\n` },
163
+ { text: `\nContent from @${relativePath}:\n` },
129
164
  { text: fileContent },
130
165
  { text: '\n--- End of content ---' },
131
166
  ],
@@ -141,7 +176,7 @@ describe('handleAtCommand', () => {
141
176
  const fileContent = 'This is the file content.';
142
177
  const relativeDirPath = path.join('path', 'to');
143
178
  const relativeFilePath = path.join(relativeDirPath, 'file.txt');
144
- const filePath = await createTestFile(path.join(testRootDir, relativeFilePath), fileContent);
179
+ await createTestFile(path.join(testRootDir, relativeFilePath), fileContent);
145
180
  const query = `@${relativeDirPath}`;
146
181
  const resolvedGlob = `${relativeDirPath}/**`;
147
182
  const result = await handleAtCommand({
@@ -156,7 +191,7 @@ describe('handleAtCommand', () => {
156
191
  processedQuery: [
157
192
  { text: `@${resolvedGlob}` },
158
193
  { text: '\n--- Content from referenced files ---' },
159
- { text: `\nContent from @${filePath}:\n` },
194
+ { text: `\nContent from @${relativeFilePath}:\n` },
160
195
  { text: fileContent },
161
196
  { text: '\n--- End of content ---' },
162
197
  ],
@@ -167,23 +202,23 @@ describe('handleAtCommand', () => {
167
202
  it('should handle query with text before and after @command', async () => {
168
203
  const fileContent = 'Markdown content.';
169
204
  const relativePath = 'doc.md';
170
- const filePath = await createTestFile(path.join(testRootDir, relativePath), fileContent);
171
- const textBefore = 'Explain this: ';
172
- const textAfter = ' in detail.';
205
+ await createTestFile(path.join(testRootDir, relativePath), fileContent);
206
+ const textBefore = 'Please read ';
207
+ const textAfter = ' and summarize.';
173
208
  const query = `${textBefore}@${relativePath}${textAfter}`;
174
209
  const result = await handleAtCommand({
175
210
  query,
176
211
  config: mockConfig,
177
212
  addItem: mockAddItem,
178
213
  onDebugMessage: mockOnDebugMessage,
179
- messageId: 128,
214
+ messageId: 127,
180
215
  signal: abortController.signal,
181
216
  });
182
217
  expect(result).toEqual({
183
218
  processedQuery: [
184
219
  { text: `${textBefore}@${relativePath}${textAfter}` },
185
220
  { text: '\n--- Content from referenced files ---' },
186
- { text: `\nContent from @${filePath}:\n` },
221
+ { text: `\nContent from @${relativePath}:\n` },
187
222
  { text: fileContent },
188
223
  { text: '\n--- End of content ---' },
189
224
  ],
@@ -193,7 +228,7 @@ describe('handleAtCommand', () => {
193
228
  it('should correctly unescape paths with escaped spaces', async () => {
194
229
  const fileContent = 'This is the file content.';
195
230
  const relativePath = path.join('path', 'to', 'my file.txt');
196
- const filePath = await createTestFile(path.join(testRootDir, relativePath), fileContent);
231
+ await createTestFile(path.join(testRootDir, relativePath), fileContent);
197
232
  const escapedPath = path.join('path', 'to', 'my\\ file.txt');
198
233
  const query = `@${escapedPath}`;
199
234
  const result = await handleAtCommand({
@@ -208,7 +243,7 @@ describe('handleAtCommand', () => {
208
243
  processedQuery: [
209
244
  { text: `@${relativePath}` },
210
245
  { text: '\n--- Content from referenced files ---' },
211
- { text: `\nContent from @${filePath}:\n` },
246
+ { text: `\nContent from @${relativePath}:\n` },
212
247
  { text: fileContent },
213
248
  { text: '\n--- End of content ---' },
214
249
  ],
@@ -223,10 +258,10 @@ describe('handleAtCommand', () => {
223
258
  it('should handle multiple @file references', async () => {
224
259
  const content1 = 'Content file1';
225
260
  const relativePath1 = 'file1.txt';
226
- const file1Path = await createTestFile(path.join(testRootDir, relativePath1), content1);
261
+ await createTestFile(path.join(testRootDir, relativePath1), content1);
227
262
  const content2 = 'Content file2';
228
263
  const relativePath2 = 'file2.md';
229
- const file2Path = await createTestFile(path.join(testRootDir, relativePath2), content2);
264
+ await createTestFile(path.join(testRootDir, relativePath2), content2);
230
265
  const query = `@${relativePath1} @${relativePath2}`;
231
266
  const result = await handleAtCommand({
232
267
  query,
@@ -240,9 +275,9 @@ describe('handleAtCommand', () => {
240
275
  processedQuery: [
241
276
  { text: query },
242
277
  { text: '\n--- Content from referenced files ---' },
243
- { text: `\nContent from @${file1Path}:\n` },
278
+ { text: `\nContent from @${relativePath1}:\n` },
244
279
  { text: content1 },
245
- { text: `\nContent from @${file2Path}:\n` },
280
+ { text: `\nContent from @${relativePath2}:\n` },
246
281
  { text: content2 },
247
282
  { text: '\n--- End of content ---' },
248
283
  ],
@@ -253,11 +288,11 @@ describe('handleAtCommand', () => {
253
288
  const text1 = 'Check ';
254
289
  const content1 = 'C1';
255
290
  const relativePath1 = 'f1.txt';
256
- const file1Path = await createTestFile(path.join(testRootDir, relativePath1), content1);
291
+ await createTestFile(path.join(testRootDir, relativePath1), content1);
257
292
  const text2 = ' and ';
258
293
  const content2 = 'C2';
259
294
  const relativePath2 = 'f2.md';
260
- const file2Path = await createTestFile(path.join(testRootDir, relativePath2), content2);
295
+ await createTestFile(path.join(testRootDir, relativePath2), content2);
261
296
  const text3 = ' please.';
262
297
  const query = `${text1}@${relativePath1}${text2}@${relativePath2}${text3}`;
263
298
  const result = await handleAtCommand({
@@ -272,9 +307,9 @@ describe('handleAtCommand', () => {
272
307
  processedQuery: [
273
308
  { text: query },
274
309
  { text: '\n--- Content from referenced files ---' },
275
- { text: `\nContent from @${file1Path}:\n` },
310
+ { text: `\nContent from @${relativePath1}:\n` },
276
311
  { text: content1 },
277
- { text: `\nContent from @${file2Path}:\n` },
312
+ { text: `\nContent from @${relativePath2}:\n` },
278
313
  { text: content2 },
279
314
  { text: '\n--- End of content ---' },
280
315
  ],
@@ -284,11 +319,11 @@ describe('handleAtCommand', () => {
284
319
  it('should handle a mix of valid, invalid, and lone @ references', async () => {
285
320
  const content1 = 'Valid content 1';
286
321
  const relativePath1 = 'valid1.txt';
287
- const file1Path = await createTestFile(path.join(testRootDir, relativePath1), content1);
322
+ await createTestFile(path.join(testRootDir, relativePath1), content1);
288
323
  const invalidFile = 'nonexistent.txt';
289
324
  const content2 = 'Globbed content';
290
325
  const relativePath2 = path.join('resolved', 'valid2.actual');
291
- const file2Path = await createTestFile(path.join(testRootDir, relativePath2), content2);
326
+ await createTestFile(path.join(testRootDir, relativePath2), content2);
292
327
  const query = `Look at @${relativePath1} then @${invalidFile} and also just @ symbol, then @${relativePath2}`;
293
328
  const result = await handleAtCommand({
294
329
  query,
@@ -309,9 +344,9 @@ describe('handleAtCommand', () => {
309
344
  .map((p) => p.text)
310
345
  .join('');
311
346
  expect(queryText).toContain('--- Content from referenced files ---');
312
- expect(queryText).toContain(`Content from @${file1Path}:`);
347
+ expect(queryText).toContain(`Content from @${relativePath1}:`);
313
348
  expect(queryText).toContain(content1);
314
- expect(queryText).toContain(`Content from @${file2Path}:`);
349
+ expect(queryText).toContain(`Content from @${relativePath2}:`);
315
350
  expect(queryText).toContain(content2);
316
351
  expect(queryText).toContain('--- End of content ---');
317
352
  expect(mockOnDebugMessage).toHaveBeenCalledWith(`Path ${invalidFile} not found directly, attempting glob search.`);
@@ -341,8 +376,8 @@ describe('handleAtCommand', () => {
341
376
  });
342
377
  it('should skip git-ignored files in @ commands', async () => {
343
378
  await createTestFile(path.join(testRootDir, '.gitignore'), 'node_modules/package.json');
344
- const gitIgnoredFile = await createTestFile(path.join(testRootDir, 'node_modules', 'package.json'), 'the file contents');
345
- const query = `@${gitIgnoredFile}`;
379
+ await createTestFile(path.join(testRootDir, 'node_modules', 'package.json'), 'the file contents');
380
+ const query = '@node_modules/package.json';
346
381
  const result = await handleAtCommand({
347
382
  query,
348
383
  config: mockConfig,
@@ -355,13 +390,13 @@ describe('handleAtCommand', () => {
355
390
  processedQuery: [{ text: query }],
356
391
  shouldProceed: true,
357
392
  });
358
- expect(mockOnDebugMessage).toHaveBeenCalledWith(`Path ${gitIgnoredFile} is git-ignored and will be skipped.`);
359
- expect(mockOnDebugMessage).toHaveBeenCalledWith(`Ignored 1 files:\nGit-ignored: ${gitIgnoredFile}`);
393
+ expect(mockOnDebugMessage).toHaveBeenCalledWith('Path node_modules/package.json is git-ignored and will be skipped.');
394
+ expect(mockOnDebugMessage).toHaveBeenCalledWith('Ignored 1 files:\nGit-ignored: node_modules/package.json');
360
395
  });
361
396
  it('should process non-git-ignored files normally', async () => {
362
397
  await createTestFile(path.join(testRootDir, '.gitignore'), 'node_modules/package.json');
363
398
  const relativePath = path.join('src', 'index.ts');
364
- const validFile = await createTestFile(path.join(testRootDir, relativePath), 'console.log("Hello world");');
399
+ await createTestFile(path.join(testRootDir, relativePath), 'console.log("Hello world");');
365
400
  const query = `@${relativePath}`;
366
401
  const result = await handleAtCommand({
367
402
  query,
@@ -375,7 +410,7 @@ describe('handleAtCommand', () => {
375
410
  processedQuery: [
376
411
  { text: `@${relativePath}` },
377
412
  { text: '\n--- Content from referenced files ---' },
378
- { text: `\nContent from @${validFile}:\n` },
413
+ { text: `\nContent from @${relativePath}:\n` },
379
414
  { text: 'console.log("Hello world");' },
380
415
  { text: '\n--- End of content ---' },
381
416
  ],
@@ -385,7 +420,7 @@ describe('handleAtCommand', () => {
385
420
  it('should handle mixed git-ignored and valid files', async () => {
386
421
  await createTestFile(path.join(testRootDir, '.gitignore'), '.env');
387
422
  const relativePath1 = 'README.md';
388
- const validFile = await createTestFile(path.join(testRootDir, relativePath1), '# Project README');
423
+ await createTestFile(path.join(testRootDir, relativePath1), '# Project README');
389
424
  const relativePath2 = '.env';
390
425
  await createTestFile(path.join(testRootDir, relativePath2), 'SECRET=123');
391
426
  const query = `@${relativePath1} @${relativePath2}`;
@@ -401,7 +436,7 @@ describe('handleAtCommand', () => {
401
436
  processedQuery: [
402
437
  { text: `@${relativePath1} @${relativePath2}` },
403
438
  { text: '\n--- Content from referenced files ---' },
404
- { text: `\nContent from @${validFile}:\n` },
439
+ { text: `\nContent from @${relativePath1}:\n` },
405
440
  { text: '# Project README' },
406
441
  { text: '\n--- End of content ---' },
407
442
  ],
@@ -411,8 +446,8 @@ describe('handleAtCommand', () => {
411
446
  expect(mockOnDebugMessage).toHaveBeenCalledWith(`Ignored 1 files:\nGit-ignored: ${relativePath2}`);
412
447
  });
413
448
  it('should always ignore .git directory files', async () => {
414
- const gitFile = await createTestFile(path.join(testRootDir, '.git', 'config'), '[core]\n\trepositoryformatversion = 0\n');
415
- const query = `@${gitFile}`;
449
+ await createTestFile(path.join(testRootDir, '.git', 'config'), '[core]\n\trepositoryformatversion = 0\n');
450
+ const query = '@.git/config';
416
451
  const result = await handleAtCommand({
417
452
  query,
418
453
  config: mockConfig,
@@ -425,8 +460,8 @@ describe('handleAtCommand', () => {
425
460
  processedQuery: [{ text: query }],
426
461
  shouldProceed: true,
427
462
  });
428
- expect(mockOnDebugMessage).toHaveBeenCalledWith(`Path ${gitFile} is git-ignored and will be skipped.`);
429
- expect(mockOnDebugMessage).toHaveBeenCalledWith(`Ignored 1 files:\nGit-ignored: ${gitFile}`);
463
+ expect(mockOnDebugMessage).toHaveBeenCalledWith('Path .git/config is git-ignored and will be skipped.');
464
+ expect(mockOnDebugMessage).toHaveBeenCalledWith('Ignored 1 files:\nGit-ignored: .git/config');
430
465
  });
431
466
  });
432
467
  describe('when recursive file search is disabled', () => {
@@ -452,8 +487,8 @@ describe('handleAtCommand', () => {
452
487
  describe('llxprt-ignore filtering', () => {
453
488
  it('should skip llxprt-ignored files in @ commands', async () => {
454
489
  await createTestFile(path.join(testRootDir, '.llxprtignore'), 'build/output.js');
455
- const llxprtIgnoredFile = await createTestFile(path.join(testRootDir, 'build', 'output.js'), 'console.log("Hello");');
456
- const query = `@${llxprtIgnoredFile}`;
490
+ await createTestFile(path.join(testRootDir, 'build', 'output.js'), 'console.log("Hello");');
491
+ const query = '@build/output.js';
457
492
  const result = await handleAtCommand({
458
493
  query,
459
494
  config: mockConfig,
@@ -466,14 +501,14 @@ describe('handleAtCommand', () => {
466
501
  processedQuery: [{ text: query }],
467
502
  shouldProceed: true,
468
503
  });
469
- expect(mockOnDebugMessage).toHaveBeenCalledWith(`Path ${llxprtIgnoredFile} is llxprt-ignored and will be skipped.`);
470
- expect(mockOnDebugMessage).toHaveBeenCalledWith(`Ignored 1 files:\nLlxprt-ignored: ${llxprtIgnoredFile}`);
504
+ expect(mockOnDebugMessage).toHaveBeenCalledWith('Path build/output.js is llxprt-ignored and will be skipped.');
505
+ expect(mockOnDebugMessage).toHaveBeenCalledWith('Ignored 1 files:\nLlxprt-ignored: build/output.js');
471
506
  });
472
507
  });
473
508
  it('should process non-ignored files when .geminiignore is present', async () => {
474
509
  await createTestFile(path.join(testRootDir, '.llxprtignore'), 'build/output.js');
475
510
  const relativePath = path.join('src', 'index.ts');
476
- const validFile = await createTestFile(path.join(testRootDir, relativePath), 'console.log("Hello world");');
511
+ await createTestFile(path.join(testRootDir, relativePath), 'console.log("Hello world");');
477
512
  const query = `@${relativePath}`;
478
513
  const result = await handleAtCommand({
479
514
  query,
@@ -487,7 +522,7 @@ describe('handleAtCommand', () => {
487
522
  processedQuery: [
488
523
  { text: `@${relativePath}` },
489
524
  { text: '\n--- Content from referenced files ---' },
490
- { text: `\nContent from @${validFile}:\n` },
525
+ { text: `\nContent from @${relativePath}:\n` },
491
526
  { text: 'console.log("Hello world");' },
492
527
  { text: '\n--- End of content ---' },
493
528
  ],
@@ -497,7 +532,7 @@ describe('handleAtCommand', () => {
497
532
  it('should handle mixed llxprt-ignored and valid files', async () => {
498
533
  await createTestFile(path.join(testRootDir, '.llxprtignore'), 'dist/bundle.js');
499
534
  const relativePath1 = path.join('src', 'main.ts');
500
- const validFile = await createTestFile(path.join(testRootDir, relativePath1), '// Main application entry');
535
+ await createTestFile(path.join(testRootDir, relativePath1), '// Main application entry');
501
536
  const relativePath2 = path.join('dist', 'bundle.js');
502
537
  await createTestFile(path.join(testRootDir, relativePath2), 'console.log("bundle");');
503
538
  const query = `@${relativePath1} @${relativePath2}`;
@@ -513,7 +548,7 @@ describe('handleAtCommand', () => {
513
548
  processedQuery: [
514
549
  { text: `@${relativePath1} @${relativePath2}` },
515
550
  { text: '\n--- Content from referenced files ---' },
516
- { text: `\nContent from @${validFile}:\n` },
551
+ { text: `\nContent from @${relativePath1}:\n` },
517
552
  { text: '// Main application entry' },
518
553
  { text: '\n--- End of content ---' },
519
554
  ],
@@ -603,8 +638,8 @@ describe('handleAtCommand', () => {
603
638
  },
604
639
  ];
605
640
  it.each(punctuationTestCases)('should terminate @path at $name', async ({ fileName, fileContent, queryTemplate, messageId }) => {
606
- const filePath = await createTestFile(path.join(testRootDir, fileName), fileContent);
607
- const query = queryTemplate(filePath);
641
+ await createTestFile(path.join(testRootDir, fileName), fileContent);
642
+ const query = queryTemplate(fileName);
608
643
  const result = await handleAtCommand({
609
644
  query,
610
645
  config: mockConfig,
@@ -613,11 +648,12 @@ describe('handleAtCommand', () => {
613
648
  messageId,
614
649
  signal: abortController.signal,
615
650
  });
651
+ const fileInQuery = fileName;
616
652
  expect(result).toEqual({
617
653
  processedQuery: [
618
654
  { text: query },
619
655
  { text: '\n--- Content from referenced files ---' },
620
- { text: `\nContent from @${filePath}:\n` },
656
+ { text: `\nContent from @${fileInQuery}:\n` },
621
657
  { text: fileContent },
622
658
  { text: '\n--- End of content ---' },
623
659
  ],
@@ -626,10 +662,10 @@ describe('handleAtCommand', () => {
626
662
  });
627
663
  it('should handle multiple @paths terminated by different punctuation', async () => {
628
664
  const content1 = 'First file';
629
- const file1Path = await createTestFile(path.join(testRootDir, 'first.txt'), content1);
665
+ await createTestFile(path.join(testRootDir, 'first.txt'), content1);
630
666
  const content2 = 'Second file';
631
- const file2Path = await createTestFile(path.join(testRootDir, 'second.txt'), content2);
632
- const query = `Compare @${file1Path}, @${file2Path}; what's different?`;
667
+ await createTestFile(path.join(testRootDir, 'second.txt'), content2);
668
+ const query = "Compare @first.txt, @second.txt; what's different?";
633
669
  const result = await handleAtCommand({
634
670
  query,
635
671
  config: mockConfig,
@@ -640,11 +676,11 @@ describe('handleAtCommand', () => {
640
676
  });
641
677
  expect(result).toEqual({
642
678
  processedQuery: [
643
- { text: `Compare @${file1Path}, @${file2Path}; what's different?` },
679
+ { text: query },
644
680
  { text: '\n--- Content from referenced files ---' },
645
- { text: `\nContent from @${file1Path}:\n` },
681
+ { text: '\nContent from @first.txt:\n' },
646
682
  { text: content1 },
647
- { text: `\nContent from @${file2Path}:\n` },
683
+ { text: '\nContent from @second.txt:\n' },
648
684
  { text: content2 },
649
685
  { text: '\n--- End of content ---' },
650
686
  ],
@@ -653,8 +689,8 @@ describe('handleAtCommand', () => {
653
689
  });
654
690
  it('should still handle escaped spaces in paths before punctuation', async () => {
655
691
  const fileContent = 'Spaced file content';
656
- const filePath = await createTestFile(path.join(testRootDir, 'spaced file.txt'), fileContent);
657
- const escapedPath = path.join(testRootDir, 'spaced\\ file.txt');
692
+ await createTestFile(path.join(testRootDir, 'spaced file.txt'), fileContent);
693
+ const escapedPath = path.join('spaced\\ file.txt');
658
694
  const query = `Check @${escapedPath}, it has spaces.`;
659
695
  const result = await handleAtCommand({
660
696
  query,
@@ -666,9 +702,9 @@ describe('handleAtCommand', () => {
666
702
  });
667
703
  expect(result).toEqual({
668
704
  processedQuery: [
669
- { text: `Check @${filePath}, it has spaces.` },
705
+ { text: 'Check @spaced file.txt, it has spaces.' },
670
706
  { text: '\n--- Content from referenced files ---' },
671
- { text: `\nContent from @${filePath}:\n` },
707
+ { text: '\nContent from @spaced file.txt:\n' },
672
708
  { text: fileContent },
673
709
  { text: '\n--- End of content ---' },
674
710
  ],
@@ -677,8 +713,8 @@ describe('handleAtCommand', () => {
677
713
  });
678
714
  it('should not break file paths with periods in extensions', async () => {
679
715
  const fileContent = 'TypeScript content';
680
- const filePath = await createTestFile(path.join(testRootDir, 'example.d.ts'), fileContent);
681
- const query = `Analyze @${filePath} for type definitions.`;
716
+ await createTestFile(path.join(testRootDir, 'example.d.ts'), fileContent);
717
+ const query = 'Analyze @example.d.ts for type definitions.';
682
718
  const result = await handleAtCommand({
683
719
  query,
684
720
  config: mockConfig,
@@ -689,9 +725,9 @@ describe('handleAtCommand', () => {
689
725
  });
690
726
  expect(result).toEqual({
691
727
  processedQuery: [
692
- { text: `Analyze @${filePath} for type definitions.` },
728
+ { text: query },
693
729
  { text: '\n--- Content from referenced files ---' },
694
- { text: `\nContent from @${filePath}:\n` },
730
+ { text: '\nContent from @example.d.ts:\n' },
695
731
  { text: fileContent },
696
732
  { text: '\n--- End of content ---' },
697
733
  ],
@@ -700,8 +736,8 @@ describe('handleAtCommand', () => {
700
736
  });
701
737
  it('should handle file paths ending with period followed by space', async () => {
702
738
  const fileContent = 'Config content';
703
- const filePath = await createTestFile(path.join(testRootDir, 'config.json'), fileContent);
704
- const query = `Check @${filePath}. This file contains settings.`;
739
+ await createTestFile(path.join(testRootDir, 'config.json'), fileContent);
740
+ const query = 'Check @config.json. This file contains settings.';
705
741
  const result = await handleAtCommand({
706
742
  query,
707
743
  config: mockConfig,
@@ -712,9 +748,9 @@ describe('handleAtCommand', () => {
712
748
  });
713
749
  expect(result).toEqual({
714
750
  processedQuery: [
715
- { text: `Check @${filePath}. This file contains settings.` },
751
+ { text: query },
716
752
  { text: '\n--- Content from referenced files ---' },
717
- { text: `\nContent from @${filePath}:\n` },
753
+ { text: '\nContent from @config.json:\n' },
718
754
  { text: fileContent },
719
755
  { text: '\n--- End of content ---' },
720
756
  ],
@@ -723,8 +759,8 @@ describe('handleAtCommand', () => {
723
759
  });
724
760
  it('should handle comma termination with complex file paths', async () => {
725
761
  const fileContent = 'Package info';
726
- const filePath = await createTestFile(path.join(testRootDir, 'package.json'), fileContent);
727
- const query = `Review @${filePath}, then check dependencies.`;
762
+ await createTestFile(path.join(testRootDir, 'package.json'), fileContent);
763
+ const query = 'Review @package.json, then check dependencies.';
728
764
  const result = await handleAtCommand({
729
765
  query,
730
766
  config: mockConfig,
@@ -735,9 +771,9 @@ describe('handleAtCommand', () => {
735
771
  });
736
772
  expect(result).toEqual({
737
773
  processedQuery: [
738
- { text: `Review @${filePath}, then check dependencies.` },
774
+ { text: query },
739
775
  { text: '\n--- Content from referenced files ---' },
740
- { text: `\nContent from @${filePath}:\n` },
776
+ { text: '\nContent from @package.json:\n' },
741
777
  { text: fileContent },
742
778
  { text: '\n--- End of content ---' },
743
779
  ],
@@ -746,8 +782,8 @@ describe('handleAtCommand', () => {
746
782
  });
747
783
  it('should not terminate at period within file name', async () => {
748
784
  const fileContent = 'Version info';
749
- const filePath = await createTestFile(path.join(testRootDir, 'version.1.2.3.txt'), fileContent);
750
- const query = `Check @${filePath} contains version information.`;
785
+ await createTestFile(path.join(testRootDir, 'version.1.2.3.txt'), fileContent);
786
+ const query = 'Check @version.1.2.3.txt contains version information.';
751
787
  const result = await handleAtCommand({
752
788
  query,
753
789
  config: mockConfig,
@@ -758,9 +794,9 @@ describe('handleAtCommand', () => {
758
794
  });
759
795
  expect(result).toEqual({
760
796
  processedQuery: [
761
- { text: `Check @${filePath} contains version information.` },
797
+ { text: query },
762
798
  { text: '\n--- Content from referenced files ---' },
763
- { text: `\nContent from @${filePath}:\n` },
799
+ { text: '\nContent from @version.1.2.3.txt:\n' },
764
800
  { text: fileContent },
765
801
  { text: '\n--- End of content ---' },
766
802
  ],
@@ -769,8 +805,8 @@ describe('handleAtCommand', () => {
769
805
  });
770
806
  it('should handle end of string termination for period and comma', async () => {
771
807
  const fileContent = 'End file content';
772
- const filePath = await createTestFile(path.join(testRootDir, 'end.txt'), fileContent);
773
- const query = `Show me @${filePath}.`;
808
+ await createTestFile(path.join(testRootDir, 'end.txt'), fileContent);
809
+ const query = 'Show me @end.txt.';
774
810
  const result = await handleAtCommand({
775
811
  query,
776
812
  config: mockConfig,
@@ -781,9 +817,9 @@ describe('handleAtCommand', () => {
781
817
  });
782
818
  expect(result).toEqual({
783
819
  processedQuery: [
784
- { text: `Show me @${filePath}.` },
820
+ { text: query },
785
821
  { text: '\n--- Content from referenced files ---' },
786
- { text: `\nContent from @${filePath}:\n` },
822
+ { text: '\nContent from @end.txt:\n' },
787
823
  { text: fileContent },
788
824
  { text: '\n--- End of content ---' },
789
825
  ],
@@ -792,8 +828,8 @@ describe('handleAtCommand', () => {
792
828
  });
793
829
  it('should handle files with special characters in names', async () => {
794
830
  const fileContent = 'File with special chars content';
795
- const filePath = await createTestFile(path.join(testRootDir, 'file$with&special#chars.txt'), fileContent);
796
- const query = `Check @${filePath} for content.`;
831
+ await createTestFile(path.join(testRootDir, 'file$with&special#chars.txt'), fileContent);
832
+ const query = 'Check @file$with&special#chars.txt for content.';
797
833
  const result = await handleAtCommand({
798
834
  query,
799
835
  config: mockConfig,
@@ -804,9 +840,9 @@ describe('handleAtCommand', () => {
804
840
  });
805
841
  expect(result).toEqual({
806
842
  processedQuery: [
807
- { text: `Check @${filePath} for content.` },
843
+ { text: 'Check @file$with&special#chars.txt for content.' },
808
844
  { text: '\n--- Content from referenced files ---' },
809
- { text: `\nContent from @${filePath}:\n` },
845
+ { text: '\nContent from @file$with&special#chars.txt:\n' },
810
846
  { text: fileContent },
811
847
  { text: '\n--- End of content ---' },
812
848
  ],
@@ -815,8 +851,8 @@ describe('handleAtCommand', () => {
815
851
  });
816
852
  it('should handle basic file names without special characters', async () => {
817
853
  const fileContent = 'Basic file content';
818
- const filePath = await createTestFile(path.join(testRootDir, 'basicfile.txt'), fileContent);
819
- const query = `Check @${filePath} please.`;
854
+ await createTestFile(path.join(testRootDir, 'basicfile.txt'), fileContent);
855
+ const query = 'Check @basicfile.txt please.';
820
856
  const result = await handleAtCommand({
821
857
  query,
822
858
  config: mockConfig,
@@ -827,9 +863,9 @@ describe('handleAtCommand', () => {
827
863
  });
828
864
  expect(result).toEqual({
829
865
  processedQuery: [
830
- { text: `Check @${filePath} please.` },
866
+ { text: query },
831
867
  { text: '\n--- Content from referenced files ---' },
832
- { text: `\nContent from @${filePath}:\n` },
868
+ { text: '\nContent from @basicfile.txt:\n' },
833
869
  { text: fileContent },
834
870
  { text: '\n--- End of content ---' },
835
871
  ],
@@ -840,8 +876,8 @@ describe('handleAtCommand', () => {
840
876
  it("should not add the user's turn to history, as that is the caller's responsibility", async () => {
841
877
  // Arrange
842
878
  const fileContent = 'This is the file content.';
843
- const filePath = await createTestFile(path.join(testRootDir, 'path', 'to', 'another-file.txt'), fileContent);
844
- const query = `A query with @${filePath}`;
879
+ await createTestFile(path.join(testRootDir, 'path', 'to', 'another-file.txt'), fileContent);
880
+ const query = 'A query with @path/to/another-file.txt';
845
881
  // Act
846
882
  await handleAtCommand({
847
883
  query,