@google/gemini-cli 0.12.0-preview.0 → 0.13.0-nightly.20251030.42c79c64

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 (90) hide show
  1. package/dist/package.json +2 -2
  2. package/dist/src/config/auth.js +0 -5
  3. package/dist/src/config/auth.js.map +1 -1
  4. package/dist/src/config/auth.test.js +1 -3
  5. package/dist/src/config/auth.test.js.map +1 -1
  6. package/dist/src/config/sandboxConfig.d.ts +1 -1
  7. package/dist/src/config/sandboxConfig.js +6 -3
  8. package/dist/src/config/sandboxConfig.js.map +1 -1
  9. package/dist/src/config/settings.js +1 -1
  10. package/dist/src/config/settings.js.map +1 -1
  11. package/dist/src/gemini.js +8 -1
  12. package/dist/src/gemini.js.map +1 -1
  13. package/dist/src/gemini.test.js +1 -0
  14. package/dist/src/gemini.test.js.map +1 -1
  15. package/dist/src/generated/git-commit.d.ts +2 -2
  16. package/dist/src/generated/git-commit.js +2 -2
  17. package/dist/src/generated/git-commit.js.map +1 -1
  18. package/dist/src/nonInteractiveCli.d.ts +9 -1
  19. package/dist/src/nonInteractiveCli.js +16 -1
  20. package/dist/src/nonInteractiveCli.js.map +1 -1
  21. package/dist/src/nonInteractiveCli.test.js +209 -103
  22. package/dist/src/nonInteractiveCli.test.js.map +1 -1
  23. package/dist/src/ui/AppContainer.js +32 -3
  24. package/dist/src/ui/AppContainer.js.map +1 -1
  25. package/dist/src/ui/auth/ApiAuthDialog.d.ts +14 -0
  26. package/dist/src/ui/auth/ApiAuthDialog.js +26 -0
  27. package/dist/src/ui/auth/ApiAuthDialog.js.map +1 -0
  28. package/dist/src/ui/auth/ApiAuthDialog.test.d.ts +6 -0
  29. package/dist/src/ui/auth/ApiAuthDialog.test.js +91 -0
  30. package/dist/src/ui/auth/ApiAuthDialog.test.js.map +1 -0
  31. package/dist/src/ui/auth/AuthDialog.js +7 -3
  32. package/dist/src/ui/auth/AuthDialog.js.map +1 -1
  33. package/dist/src/ui/auth/AuthDialog.test.js +1 -1
  34. package/dist/src/ui/auth/AuthDialog.test.js.map +1 -1
  35. package/dist/src/ui/auth/useAuth.d.ts +2 -0
  36. package/dist/src/ui/auth/useAuth.js +31 -2
  37. package/dist/src/ui/auth/useAuth.js.map +1 -1
  38. package/dist/src/ui/components/DialogManager.js +4 -0
  39. package/dist/src/ui/components/DialogManager.js.map +1 -1
  40. package/dist/src/ui/components/messages/Todo.js +27 -5
  41. package/dist/src/ui/components/messages/Todo.js.map +1 -1
  42. package/dist/src/ui/components/messages/Todo.test.js +19 -7
  43. package/dist/src/ui/components/messages/Todo.test.js.map +1 -1
  44. package/dist/src/ui/components/shared/TextInput.d.ts +15 -0
  45. package/dist/src/ui/components/shared/TextInput.js +38 -0
  46. package/dist/src/ui/components/shared/TextInput.js.map +1 -0
  47. package/dist/src/ui/components/shared/TextInput.test.d.ts +6 -0
  48. package/dist/src/ui/components/shared/TextInput.test.js +242 -0
  49. package/dist/src/ui/components/shared/TextInput.test.js.map +1 -0
  50. package/dist/src/ui/components/shared/text-buffer.d.ts +8 -2
  51. package/dist/src/ui/components/shared/text-buffer.js +28 -13
  52. package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
  53. package/dist/src/ui/components/shared/text-buffer.test.js +137 -0
  54. package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -1
  55. package/dist/src/ui/contexts/KeypressContext.js +8 -29
  56. package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
  57. package/dist/src/ui/contexts/KeypressContext.test.js +84 -68
  58. package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
  59. package/dist/src/ui/contexts/UIActionsContext.d.ts +2 -0
  60. package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
  61. package/dist/src/ui/contexts/UIStateContext.d.ts +2 -0
  62. package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
  63. package/dist/src/ui/hooks/atCommandProcessor.js +29 -7
  64. package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
  65. package/dist/src/ui/hooks/atCommandProcessor.test.js +162 -64
  66. package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
  67. package/dist/src/ui/hooks/useSlashCompletion.js +2 -2
  68. package/dist/src/ui/hooks/useSlashCompletion.js.map +1 -1
  69. package/dist/src/ui/types.d.ts +1 -0
  70. package/dist/src/ui/types.js +2 -0
  71. package/dist/src/ui/types.js.map +1 -1
  72. package/dist/src/ui/utils/clipboardUtils.js +2 -2
  73. package/dist/src/ui/utils/clipboardUtils.js.map +1 -1
  74. package/dist/src/ui/utils/updateCheck.js +6 -3
  75. package/dist/src/ui/utils/updateCheck.js.map +1 -1
  76. package/dist/src/ui/utils/updateCheck.test.js +5 -1
  77. package/dist/src/ui/utils/updateCheck.test.js.map +1 -1
  78. package/dist/src/utils/commentJson.js +2 -2
  79. package/dist/src/utils/commentJson.js.map +1 -1
  80. package/dist/src/utils/commentJson.test.js +7 -6
  81. package/dist/src/utils/commentJson.test.js.map +1 -1
  82. package/dist/src/utils/version.js +6 -2
  83. package/dist/src/utils/version.js.map +1 -1
  84. package/dist/src/zed-integration/acp.js +2 -1
  85. package/dist/src/zed-integration/acp.js.map +1 -1
  86. package/dist/tsconfig.tsbuildinfo +1 -1
  87. package/package.json +3 -3
  88. package/dist/src/utils/package.d.ts +0 -12
  89. package/dist/src/utils/package.js +0 -24
  90. package/dist/src/utils/package.js.map +0 -1
@@ -23,6 +23,9 @@ describe('handleAtCommand', () => {
23
23
  await fsPromises.writeFile(fullPath, fileContents);
24
24
  return path.resolve(testRootDir, fullPath);
25
25
  }
26
+ function getRelativePath(absolutePath) {
27
+ return path.relative(testRootDir, absolutePath);
28
+ }
26
29
  beforeEach(async () => {
27
30
  vi.resetAllMocks();
28
31
  testRootDir = await fsPromises.mkdtemp(path.join(os.tmpdir(), 'folder-structure-test-'));
@@ -103,6 +106,7 @@ describe('handleAtCommand', () => {
103
106
  it('should process a valid text file path', async () => {
104
107
  const fileContent = 'This is the file content.';
105
108
  const filePath = await createTestFile(path.join(testRootDir, 'path', 'to', 'file.txt'), fileContent);
109
+ const relativePath = getRelativePath(filePath);
106
110
  const query = `@${filePath}`;
107
111
  const result = await handleAtCommand({
108
112
  query,
@@ -114,9 +118,9 @@ describe('handleAtCommand', () => {
114
118
  });
115
119
  expect(result).toEqual({
116
120
  processedQuery: [
117
- { text: `@${filePath}` },
121
+ { text: `@${relativePath}` },
118
122
  { text: '\n--- Content from referenced files ---' },
119
- { text: `\nContent from @${filePath}:\n` },
123
+ { text: `\nContent from @${relativePath}:\n` },
120
124
  { text: fileContent },
121
125
  { text: '\n--- End of content ---' },
122
126
  ],
@@ -131,8 +135,10 @@ describe('handleAtCommand', () => {
131
135
  const fileContent = 'This is the file content.';
132
136
  const filePath = await createTestFile(path.join(testRootDir, 'path', 'to', 'file.txt'), fileContent);
133
137
  const dirPath = path.dirname(filePath);
138
+ const relativeDirPath = getRelativePath(dirPath);
139
+ const relativeFilePath = getRelativePath(filePath);
134
140
  const query = `@${dirPath}`;
135
- const resolvedGlob = `${dirPath}/**`;
141
+ const resolvedGlob = path.join(relativeDirPath, '**');
136
142
  const result = await handleAtCommand({
137
143
  query,
138
144
  config: mockConfig,
@@ -145,7 +151,7 @@ describe('handleAtCommand', () => {
145
151
  processedQuery: [
146
152
  { text: `@${resolvedGlob}` },
147
153
  { text: '\n--- Content from referenced files ---' },
148
- { text: `\nContent from @${filePath}:\n` },
154
+ { text: `\nContent from @${relativeFilePath}:\n` },
149
155
  { text: fileContent },
150
156
  { text: '\n--- End of content ---' },
151
157
  ],
@@ -156,6 +162,7 @@ describe('handleAtCommand', () => {
156
162
  it('should handle query with text before and after @command', async () => {
157
163
  const fileContent = 'Markdown content.';
158
164
  const filePath = await createTestFile(path.join(testRootDir, 'doc.md'), fileContent);
165
+ const relativePath = getRelativePath(filePath);
159
166
  const textBefore = 'Explain this: ';
160
167
  const textAfter = ' in detail.';
161
168
  const query = `${textBefore}@${filePath}${textAfter}`;
@@ -169,9 +176,9 @@ describe('handleAtCommand', () => {
169
176
  });
170
177
  expect(result).toEqual({
171
178
  processedQuery: [
172
- { text: `${textBefore}@${filePath}${textAfter}` },
179
+ { text: `${textBefore}@${relativePath}${textAfter}` },
173
180
  { text: '\n--- Content from referenced files ---' },
174
- { text: `\nContent from @${filePath}:\n` },
181
+ { text: `\nContent from @${relativePath}:\n` },
175
182
  { text: fileContent },
176
183
  { text: '\n--- End of content ---' },
177
184
  ],
@@ -193,9 +200,9 @@ describe('handleAtCommand', () => {
193
200
  });
194
201
  expect(result).toEqual({
195
202
  processedQuery: [
196
- { text: `@${filePath}` },
203
+ { text: `@${getRelativePath(filePath)}` },
197
204
  { text: '\n--- Content from referenced files ---' },
198
- { text: `\nContent from @${filePath}:\n` },
205
+ { text: `\nContent from @${getRelativePath(filePath)}:\n` },
199
206
  { text: fileContent },
200
207
  { text: '\n--- End of content ---' },
201
208
  ],
@@ -222,11 +229,13 @@ describe('handleAtCommand', () => {
222
229
  });
223
230
  expect(result).toEqual({
224
231
  processedQuery: [
225
- { text: query },
232
+ {
233
+ text: `@${getRelativePath(file1Path)} @${getRelativePath(file2Path)}`,
234
+ },
226
235
  { text: '\n--- Content from referenced files ---' },
227
- { text: `\nContent from @${file1Path}:\n` },
236
+ { text: `\nContent from @${getRelativePath(file1Path)}:\n` },
228
237
  { text: content1 },
229
- { text: `\nContent from @${file2Path}:\n` },
238
+ { text: `\nContent from @${getRelativePath(file2Path)}:\n` },
230
239
  { text: content2 },
231
240
  { text: '\n--- End of content ---' },
232
241
  ],
@@ -252,11 +261,13 @@ describe('handleAtCommand', () => {
252
261
  });
253
262
  expect(result).toEqual({
254
263
  processedQuery: [
255
- { text: query },
264
+ {
265
+ text: `${text1}@${getRelativePath(file1Path)}${text2}@${getRelativePath(file2Path)}${text3}`,
266
+ },
256
267
  { text: '\n--- Content from referenced files ---' },
257
- { text: `\nContent from @${file1Path}:\n` },
268
+ { text: `\nContent from @${getRelativePath(file1Path)}:\n` },
258
269
  { text: content1 },
259
- { text: `\nContent from @${file2Path}:\n` },
270
+ { text: `\nContent from @${getRelativePath(file2Path)}:\n` },
260
271
  { text: content2 },
261
272
  { text: '\n--- End of content ---' },
262
273
  ],
@@ -281,12 +292,12 @@ describe('handleAtCommand', () => {
281
292
  expect(result).toEqual({
282
293
  processedQuery: [
283
294
  {
284
- text: `Look at @${file1Path} then @${invalidFile} and also just @ symbol, then @${file2Path}`,
295
+ text: `Look at @${getRelativePath(file1Path)} then @${invalidFile} and also just @ symbol, then @${getRelativePath(file2Path)}`,
285
296
  },
286
297
  { text: '\n--- Content from referenced files ---' },
287
- { text: `\nContent from @${file2Path}:\n` },
298
+ { text: `\nContent from @${getRelativePath(file2Path)}:\n` },
288
299
  { text: content2 },
289
- { text: `\nContent from @${file1Path}:\n` },
300
+ { text: `\nContent from @${getRelativePath(file1Path)}:\n` },
290
301
  { text: content1 },
291
302
  { text: '\n--- End of content ---' },
292
303
  ],
@@ -350,9 +361,9 @@ describe('handleAtCommand', () => {
350
361
  });
351
362
  expect(result).toEqual({
352
363
  processedQuery: [
353
- { text: `@${validFile}` },
364
+ { text: `@${getRelativePath(validFile)}` },
354
365
  { text: '\n--- Content from referenced files ---' },
355
- { text: `\nContent from @${validFile}:\n` },
366
+ { text: `\nContent from @${getRelativePath(validFile)}:\n` },
356
367
  { text: 'console.log("Hello world");' },
357
368
  { text: '\n--- End of content ---' },
358
369
  ],
@@ -374,9 +385,9 @@ describe('handleAtCommand', () => {
374
385
  });
375
386
  expect(result).toEqual({
376
387
  processedQuery: [
377
- { text: `@${validFile} @${gitIgnoredFile}` },
388
+ { text: `@${getRelativePath(validFile)} @${gitIgnoredFile}` },
378
389
  { text: '\n--- Content from referenced files ---' },
379
- { text: `\nContent from @${validFile}:\n` },
390
+ { text: `\nContent from @${getRelativePath(validFile)}:\n` },
380
391
  { text: '# Project README' },
381
392
  { text: '\n--- End of content ---' },
382
393
  ],
@@ -459,9 +470,9 @@ describe('handleAtCommand', () => {
459
470
  });
460
471
  expect(result).toEqual({
461
472
  processedQuery: [
462
- { text: `@${validFile}` },
473
+ { text: `@${getRelativePath(validFile)}` },
463
474
  { text: '\n--- Content from referenced files ---' },
464
- { text: `\nContent from @${validFile}:\n` },
475
+ { text: `\nContent from @${getRelativePath(validFile)}:\n` },
465
476
  { text: 'console.log("Hello world");' },
466
477
  { text: '\n--- End of content ---' },
467
478
  ],
@@ -483,9 +494,9 @@ describe('handleAtCommand', () => {
483
494
  });
484
495
  expect(result).toEqual({
485
496
  processedQuery: [
486
- { text: `@${validFile} @${geminiIgnoredFile}` },
497
+ { text: `@${getRelativePath(validFile)} @${geminiIgnoredFile}` },
487
498
  { text: '\n--- Content from referenced files ---' },
488
- { text: `\nContent from @${validFile}:\n` },
499
+ { text: `\nContent from @${getRelativePath(validFile)}:\n` },
489
500
  { text: '// Main application entry' },
490
501
  { text: '\n--- End of content ---' },
491
502
  ],
@@ -500,77 +511,77 @@ describe('handleAtCommand', () => {
500
511
  name: 'comma',
501
512
  fileName: 'test.txt',
502
513
  fileContent: 'File content here',
503
- queryTemplate: (filePath) => `Look at @${filePath}, then explain it.`,
514
+ queryTemplate: (filePath) => `Look at @${getRelativePath(filePath)}, then explain it.`,
504
515
  messageId: 400,
505
516
  },
506
517
  {
507
518
  name: 'period',
508
519
  fileName: 'readme.md',
509
520
  fileContent: 'File content here',
510
- queryTemplate: (filePath) => `Check @${filePath}. What does it say?`,
521
+ queryTemplate: (filePath) => `Check @${getRelativePath(filePath)}. What does it say?`,
511
522
  messageId: 401,
512
523
  },
513
524
  {
514
525
  name: 'semicolon',
515
526
  fileName: 'example.js',
516
527
  fileContent: 'Code example',
517
- queryTemplate: (filePath) => `Review @${filePath}; check for bugs.`,
528
+ queryTemplate: (filePath) => `Review @${getRelativePath(filePath)}; check for bugs.`,
518
529
  messageId: 402,
519
530
  },
520
531
  {
521
532
  name: 'exclamation mark',
522
533
  fileName: 'important.txt',
523
534
  fileContent: 'Important content',
524
- queryTemplate: (filePath) => `Look at @${filePath}! This is critical.`,
535
+ queryTemplate: (filePath) => `Look at @${getRelativePath(filePath)}! This is critical.`,
525
536
  messageId: 403,
526
537
  },
527
538
  {
528
539
  name: 'question mark',
529
540
  fileName: 'config.json',
530
541
  fileContent: 'Config settings',
531
- queryTemplate: (filePath) => `What is in @${filePath}? Please explain.`,
542
+ queryTemplate: (filePath) => `What is in @${getRelativePath(filePath)}? Please explain.`,
532
543
  messageId: 404,
533
544
  },
534
545
  {
535
546
  name: 'opening parenthesis',
536
547
  fileName: 'func.ts',
537
548
  fileContent: 'Function definition',
538
- queryTemplate: (filePath) => `Analyze @${filePath}(the main function).`,
549
+ queryTemplate: (filePath) => `Analyze @${getRelativePath(filePath)}(the main function).`,
539
550
  messageId: 405,
540
551
  },
541
552
  {
542
553
  name: 'closing parenthesis',
543
554
  fileName: 'data.json',
544
555
  fileContent: 'Test data',
545
- queryTemplate: (filePath) => `Use data from @${filePath}) for testing.`,
556
+ queryTemplate: (filePath) => `Use data from @${getRelativePath(filePath)}) for testing.`,
546
557
  messageId: 406,
547
558
  },
548
559
  {
549
560
  name: 'opening square bracket',
550
561
  fileName: 'array.js',
551
562
  fileContent: 'Array data',
552
- queryTemplate: (filePath) => `Check @${filePath}[0] for the first element.`,
563
+ queryTemplate: (filePath) => `Check @${getRelativePath(filePath)}[0] for the first element.`,
553
564
  messageId: 407,
554
565
  },
555
566
  {
556
567
  name: 'closing square bracket',
557
568
  fileName: 'list.md',
558
569
  fileContent: 'List content',
559
- queryTemplate: (filePath) => `Review item @${filePath}] from the list.`,
570
+ queryTemplate: (filePath) => `Review item @${getRelativePath(filePath)}] from the list.`,
560
571
  messageId: 408,
561
572
  },
562
573
  {
563
574
  name: 'opening curly brace',
564
575
  fileName: 'object.ts',
565
576
  fileContent: 'Object definition',
566
- queryTemplate: (filePath) => `Parse @${filePath}{prop1: value1}.`,
577
+ queryTemplate: (filePath) => `Parse @${getRelativePath(filePath)}{prop1: value1}.`,
567
578
  messageId: 409,
568
579
  },
569
580
  {
570
581
  name: 'closing curly brace',
571
582
  fileName: 'config.yaml',
572
583
  fileContent: 'Configuration',
573
- queryTemplate: (filePath) => `Use settings from @${filePath}} for deployment.`,
584
+ queryTemplate: (filePath) => `Use settings from @${getRelativePath(filePath)}} for deployment.`,
574
585
  messageId: 410,
575
586
  },
576
587
  ];
@@ -589,7 +600,7 @@ describe('handleAtCommand', () => {
589
600
  processedQuery: [
590
601
  { text: query },
591
602
  { text: '\n--- Content from referenced files ---' },
592
- { text: `\nContent from @${filePath}:\n` },
603
+ { text: `\nContent from @${getRelativePath(filePath)}:\n` },
593
604
  { text: fileContent },
594
605
  { text: '\n--- End of content ---' },
595
606
  ],
@@ -612,11 +623,13 @@ describe('handleAtCommand', () => {
612
623
  });
613
624
  expect(result).toEqual({
614
625
  processedQuery: [
615
- { text: `Compare @${file1Path}, @${file2Path}; what's different?` },
626
+ {
627
+ text: `Compare @${getRelativePath(file1Path)}, @${getRelativePath(file2Path)}; what's different?`,
628
+ },
616
629
  { text: '\n--- Content from referenced files ---' },
617
- { text: `\nContent from @${file1Path}:\n` },
630
+ { text: `\nContent from @${getRelativePath(file1Path)}:\n` },
618
631
  { text: content1 },
619
- { text: `\nContent from @${file2Path}:\n` },
632
+ { text: `\nContent from @${getRelativePath(file2Path)}:\n` },
620
633
  { text: content2 },
621
634
  { text: '\n--- End of content ---' },
622
635
  ],
@@ -638,9 +651,9 @@ describe('handleAtCommand', () => {
638
651
  });
639
652
  expect(result).toEqual({
640
653
  processedQuery: [
641
- { text: `Check @${filePath}, it has spaces.` },
654
+ { text: `Check @${getRelativePath(filePath)}, it has spaces.` },
642
655
  { text: '\n--- Content from referenced files ---' },
643
- { text: `\nContent from @${filePath}:\n` },
656
+ { text: `\nContent from @${getRelativePath(filePath)}:\n` },
644
657
  { text: fileContent },
645
658
  { text: '\n--- End of content ---' },
646
659
  ],
@@ -650,7 +663,7 @@ describe('handleAtCommand', () => {
650
663
  it('should not break file paths with periods in extensions', async () => {
651
664
  const fileContent = 'TypeScript content';
652
665
  const filePath = await createTestFile(path.join(testRootDir, 'example.d.ts'), fileContent);
653
- const query = `Analyze @${filePath} for type definitions.`;
666
+ const query = `Analyze @${getRelativePath(filePath)} for type definitions.`;
654
667
  const result = await handleAtCommand({
655
668
  query,
656
669
  config: mockConfig,
@@ -661,9 +674,11 @@ describe('handleAtCommand', () => {
661
674
  });
662
675
  expect(result).toEqual({
663
676
  processedQuery: [
664
- { text: `Analyze @${filePath} for type definitions.` },
677
+ {
678
+ text: `Analyze @${getRelativePath(filePath)} for type definitions.`,
679
+ },
665
680
  { text: '\n--- Content from referenced files ---' },
666
- { text: `\nContent from @${filePath}:\n` },
681
+ { text: `\nContent from @${getRelativePath(filePath)}:\n` },
667
682
  { text: fileContent },
668
683
  { text: '\n--- End of content ---' },
669
684
  ],
@@ -673,7 +688,7 @@ describe('handleAtCommand', () => {
673
688
  it('should handle file paths ending with period followed by space', async () => {
674
689
  const fileContent = 'Config content';
675
690
  const filePath = await createTestFile(path.join(testRootDir, 'config.json'), fileContent);
676
- const query = `Check @${filePath}. This file contains settings.`;
691
+ const query = `Check @${getRelativePath(filePath)}. This file contains settings.`;
677
692
  const result = await handleAtCommand({
678
693
  query,
679
694
  config: mockConfig,
@@ -684,9 +699,11 @@ describe('handleAtCommand', () => {
684
699
  });
685
700
  expect(result).toEqual({
686
701
  processedQuery: [
687
- { text: `Check @${filePath}. This file contains settings.` },
702
+ {
703
+ text: `Check @${getRelativePath(filePath)}. This file contains settings.`,
704
+ },
688
705
  { text: '\n--- Content from referenced files ---' },
689
- { text: `\nContent from @${filePath}:\n` },
706
+ { text: `\nContent from @${getRelativePath(filePath)}:\n` },
690
707
  { text: fileContent },
691
708
  { text: '\n--- End of content ---' },
692
709
  ],
@@ -696,7 +713,7 @@ describe('handleAtCommand', () => {
696
713
  it('should handle comma termination with complex file paths', async () => {
697
714
  const fileContent = 'Package info';
698
715
  const filePath = await createTestFile(path.join(testRootDir, 'package.json'), fileContent);
699
- const query = `Review @${filePath}, then check dependencies.`;
716
+ const query = `Review @${getRelativePath(filePath)}, then check dependencies.`;
700
717
  const result = await handleAtCommand({
701
718
  query,
702
719
  config: mockConfig,
@@ -707,9 +724,11 @@ describe('handleAtCommand', () => {
707
724
  });
708
725
  expect(result).toEqual({
709
726
  processedQuery: [
710
- { text: `Review @${filePath}, then check dependencies.` },
727
+ {
728
+ text: `Review @${getRelativePath(filePath)}, then check dependencies.`,
729
+ },
711
730
  { text: '\n--- Content from referenced files ---' },
712
- { text: `\nContent from @${filePath}:\n` },
731
+ { text: `\nContent from @${getRelativePath(filePath)}:\n` },
713
732
  { text: fileContent },
714
733
  { text: '\n--- End of content ---' },
715
734
  ],
@@ -719,7 +738,7 @@ describe('handleAtCommand', () => {
719
738
  it('should not terminate at period within file name', async () => {
720
739
  const fileContent = 'Version info';
721
740
  const filePath = await createTestFile(path.join(testRootDir, 'version.1.2.3.txt'), fileContent);
722
- const query = `Check @${filePath} contains version information.`;
741
+ const query = `Check @${getRelativePath(filePath)} contains version information.`;
723
742
  const result = await handleAtCommand({
724
743
  query,
725
744
  config: mockConfig,
@@ -730,9 +749,11 @@ describe('handleAtCommand', () => {
730
749
  });
731
750
  expect(result).toEqual({
732
751
  processedQuery: [
733
- { text: `Check @${filePath} contains version information.` },
752
+ {
753
+ text: `Check @${getRelativePath(filePath)} contains version information.`,
754
+ },
734
755
  { text: '\n--- Content from referenced files ---' },
735
- { text: `\nContent from @${filePath}:\n` },
756
+ { text: `\nContent from @${getRelativePath(filePath)}:\n` },
736
757
  { text: fileContent },
737
758
  { text: '\n--- End of content ---' },
738
759
  ],
@@ -742,7 +763,7 @@ describe('handleAtCommand', () => {
742
763
  it('should handle end of string termination for period and comma', async () => {
743
764
  const fileContent = 'End file content';
744
765
  const filePath = await createTestFile(path.join(testRootDir, 'end.txt'), fileContent);
745
- const query = `Show me @${filePath}.`;
766
+ const query = `Show me @${getRelativePath(filePath)}.`;
746
767
  const result = await handleAtCommand({
747
768
  query,
748
769
  config: mockConfig,
@@ -753,9 +774,9 @@ describe('handleAtCommand', () => {
753
774
  });
754
775
  expect(result).toEqual({
755
776
  processedQuery: [
756
- { text: `Show me @${filePath}.` },
777
+ { text: `Show me @${getRelativePath(filePath)}.` },
757
778
  { text: '\n--- Content from referenced files ---' },
758
- { text: `\nContent from @${filePath}:\n` },
779
+ { text: `\nContent from @${getRelativePath(filePath)}:\n` },
759
780
  { text: fileContent },
760
781
  { text: '\n--- End of content ---' },
761
782
  ],
@@ -765,7 +786,7 @@ describe('handleAtCommand', () => {
765
786
  it('should handle files with special characters in names', async () => {
766
787
  const fileContent = 'File with special chars content';
767
788
  const filePath = await createTestFile(path.join(testRootDir, 'file$with&special#chars.txt'), fileContent);
768
- const query = `Check @${filePath} for content.`;
789
+ const query = `Check @${getRelativePath(filePath)} for content.`;
769
790
  const result = await handleAtCommand({
770
791
  query,
771
792
  config: mockConfig,
@@ -776,9 +797,9 @@ describe('handleAtCommand', () => {
776
797
  });
777
798
  expect(result).toEqual({
778
799
  processedQuery: [
779
- { text: `Check @${filePath} for content.` },
800
+ { text: `Check @${getRelativePath(filePath)} for content.` },
780
801
  { text: '\n--- Content from referenced files ---' },
781
- { text: `\nContent from @${filePath}:\n` },
802
+ { text: `\nContent from @${getRelativePath(filePath)}:\n` },
782
803
  { text: fileContent },
783
804
  { text: '\n--- End of content ---' },
784
805
  ],
@@ -788,7 +809,7 @@ describe('handleAtCommand', () => {
788
809
  it('should handle basic file names without special characters', async () => {
789
810
  const fileContent = 'Basic file content';
790
811
  const filePath = await createTestFile(path.join(testRootDir, 'basicfile.txt'), fileContent);
791
- const query = `Check @${filePath} please.`;
812
+ const query = `Check @${getRelativePath(filePath)} please.`;
792
813
  const result = await handleAtCommand({
793
814
  query,
794
815
  config: mockConfig,
@@ -799,21 +820,98 @@ describe('handleAtCommand', () => {
799
820
  });
800
821
  expect(result).toEqual({
801
822
  processedQuery: [
802
- { text: `Check @${filePath} please.` },
823
+ { text: `Check @${getRelativePath(filePath)} please.` },
824
+ { text: '\n--- Content from referenced files ---' },
825
+ { text: `\nContent from @${getRelativePath(filePath)}:\n` },
826
+ { text: fileContent },
827
+ { text: '\n--- End of content ---' },
828
+ ],
829
+ shouldProceed: true,
830
+ });
831
+ });
832
+ });
833
+ describe('absolute path handling', () => {
834
+ it('should handle absolute file paths correctly', async () => {
835
+ const fileContent = 'console.log("This is an absolute path test");';
836
+ const relativePath = path.join('src', 'absolute-test.ts');
837
+ const absolutePath = await createTestFile(path.join(testRootDir, relativePath), fileContent);
838
+ const query = `Check @${absolutePath} please.`;
839
+ const result = await handleAtCommand({
840
+ query,
841
+ config: mockConfig,
842
+ addItem: mockAddItem,
843
+ onDebugMessage: mockOnDebugMessage,
844
+ messageId: 500,
845
+ signal: abortController.signal,
846
+ });
847
+ expect(result).toEqual({
848
+ processedQuery: [
849
+ { text: `Check @${relativePath} please.` },
803
850
  { text: '\n--- Content from referenced files ---' },
804
- { text: `\nContent from @${filePath}:\n` },
851
+ { text: `\nContent from @${relativePath}:\n` },
805
852
  { text: fileContent },
806
853
  { text: '\n--- End of content ---' },
807
854
  ],
808
855
  shouldProceed: true,
809
856
  });
857
+ expect(mockOnDebugMessage).toHaveBeenCalledWith(expect.stringContaining(`using relative path: ${relativePath}`));
858
+ });
859
+ it('should handle absolute directory paths correctly', async () => {
860
+ const fileContent = 'export default function test() { return "absolute dir test"; }';
861
+ const subDirPath = path.join('src', 'utils');
862
+ const fileName = 'helper.ts';
863
+ await createTestFile(path.join(testRootDir, subDirPath, fileName), fileContent);
864
+ const absoluteDirPath = path.join(testRootDir, subDirPath);
865
+ const query = `Check @${absoluteDirPath} please.`;
866
+ const result = await handleAtCommand({
867
+ query,
868
+ config: mockConfig,
869
+ addItem: mockAddItem,
870
+ onDebugMessage: mockOnDebugMessage,
871
+ messageId: 501,
872
+ signal: abortController.signal,
873
+ });
874
+ expect(result.shouldProceed).toBe(true);
875
+ expect(result.processedQuery).toEqual(expect.arrayContaining([
876
+ { text: `Check @${path.join(subDirPath, '**')} please.` },
877
+ expect.objectContaining({
878
+ text: '\n--- Content from referenced files ---',
879
+ }),
880
+ ]));
881
+ expect(mockOnDebugMessage).toHaveBeenCalledWith(expect.stringContaining(`using glob: ${path.join(subDirPath, '**')}`));
882
+ });
883
+ it('should skip absolute paths outside workspace', async () => {
884
+ const outsidePath = '/tmp/outside-workspace.txt';
885
+ const query = `Check @${outsidePath} please.`;
886
+ const mockWorkspaceContext = {
887
+ isPathWithinWorkspace: vi.fn((path) => path.startsWith(testRootDir)),
888
+ getDirectories: () => [testRootDir],
889
+ addDirectory: vi.fn(),
890
+ getInitialDirectories: () => [testRootDir],
891
+ setDirectories: vi.fn(),
892
+ onDirectoriesChanged: vi.fn(() => () => { }),
893
+ };
894
+ mockConfig.getWorkspaceContext = () => mockWorkspaceContext;
895
+ const result = await handleAtCommand({
896
+ query,
897
+ config: mockConfig,
898
+ addItem: mockAddItem,
899
+ onDebugMessage: mockOnDebugMessage,
900
+ messageId: 502,
901
+ signal: abortController.signal,
902
+ });
903
+ expect(result).toEqual({
904
+ processedQuery: [{ text: `Check @${outsidePath} please.` }],
905
+ shouldProceed: true,
906
+ });
907
+ expect(mockOnDebugMessage).toHaveBeenCalledWith(`Path ${outsidePath} is not in the workspace and will be skipped.`);
810
908
  });
811
909
  });
812
910
  it("should not add the user's turn to history, as that is the caller's responsibility", async () => {
813
911
  // Arrange
814
912
  const fileContent = 'This is the file content.';
815
913
  const filePath = await createTestFile(path.join(testRootDir, 'path', 'to', 'another-file.txt'), fileContent);
816
- const query = `A query with @${filePath}`;
914
+ const query = `A query with @${getRelativePath(filePath)}`;
817
915
  // Act
818
916
  await handleAtCommand({
819
917
  query,