@vybestack/llxprt-code 0.8.0-nightly.260111.af7260fe6 → 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.
- package/dist/package.json +3 -3
- package/dist/src/generated/git-commit.d.ts +1 -1
- package/dist/src/generated/git-commit.js +1 -1
- package/dist/src/runtime/runtimeSettings.d.ts +1 -0
- package/dist/src/runtime/runtimeSettings.js +4 -0
- package/dist/src/runtime/runtimeSettings.js.map +1 -1
- package/dist/src/ui/AppContainer.js +67 -0
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/commands/profileCommand.js +111 -9
- package/dist/src/ui/commands/profileCommand.js.map +1 -1
- package/dist/src/ui/commands/profileCommand.test.js +3 -6
- package/dist/src/ui/commands/profileCommand.test.js.map +1 -1
- package/dist/src/ui/commands/subagentCommand.js +58 -244
- package/dist/src/ui/commands/subagentCommand.js.map +1 -1
- package/dist/src/ui/commands/test/subagentCommand.schema.test.js +4 -3
- package/dist/src/ui/commands/test/subagentCommand.schema.test.js.map +1 -1
- package/dist/src/ui/commands/test/subagentCommand.test.js +44 -124
- package/dist/src/ui/commands/test/subagentCommand.test.js.map +1 -1
- package/dist/src/ui/commands/types.d.ts +43 -2
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/components/DialogManager.js +17 -0
- package/dist/src/ui/components/DialogManager.js.map +1 -1
- package/dist/src/ui/components/ProfileDetailDialog.d.ts +22 -0
- package/dist/src/ui/components/ProfileDetailDialog.js +113 -0
- package/dist/src/ui/components/ProfileDetailDialog.js.map +1 -0
- package/dist/src/ui/components/ProfileInlineEditor.d.ts +16 -0
- package/dist/src/ui/components/ProfileInlineEditor.js +216 -0
- package/dist/src/ui/components/ProfileInlineEditor.js.map +1 -0
- package/dist/src/ui/components/ProfileListDialog.d.ts +26 -0
- package/dist/src/ui/components/ProfileListDialog.js +172 -0
- package/dist/src/ui/components/ProfileListDialog.js.map +1 -0
- package/dist/src/ui/components/ProviderDialog.js +1 -1
- package/dist/src/ui/components/ProviderDialog.js.map +1 -1
- package/dist/src/ui/components/ProviderModelDialog.js +1 -1
- package/dist/src/ui/components/ProviderModelDialog.js.map +1 -1
- package/dist/src/ui/components/SubagentManagement/ProfileAttachmentWizard.d.ts +24 -0
- package/dist/src/ui/components/SubagentManagement/ProfileAttachmentWizard.js +102 -0
- package/dist/src/ui/components/SubagentManagement/ProfileAttachmentWizard.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/SubagentCreationWizard.d.ts +14 -0
- package/dist/src/ui/components/SubagentManagement/SubagentCreationWizard.js +179 -0
- package/dist/src/ui/components/SubagentManagement/SubagentCreationWizard.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/SubagentDeleteDialog.d.ts +15 -0
- package/dist/src/ui/components/SubagentManagement/SubagentDeleteDialog.js +47 -0
- package/dist/src/ui/components/SubagentManagement/SubagentDeleteDialog.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/SubagentEditForm.d.ts +18 -0
- package/dist/src/ui/components/SubagentManagement/SubagentEditForm.js +111 -0
- package/dist/src/ui/components/SubagentManagement/SubagentEditForm.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/SubagentListMenu.d.ts +19 -0
- package/dist/src/ui/components/SubagentManagement/SubagentListMenu.js +137 -0
- package/dist/src/ui/components/SubagentManagement/SubagentListMenu.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/SubagentMainMenu.d.ts +13 -0
- package/dist/src/ui/components/SubagentManagement/SubagentMainMenu.js +14 -0
- package/dist/src/ui/components/SubagentManagement/SubagentMainMenu.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/SubagentManagerDialog.d.ts +8 -0
- package/dist/src/ui/components/SubagentManagement/SubagentManagerDialog.js +293 -0
- package/dist/src/ui/components/SubagentManagement/SubagentManagerDialog.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/SubagentShowView.d.ts +15 -0
- package/dist/src/ui/components/SubagentManagement/SubagentShowView.js +35 -0
- package/dist/src/ui/components/SubagentManagement/SubagentShowView.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/index.d.ts +14 -0
- package/dist/src/ui/components/SubagentManagement/index.js +15 -0
- package/dist/src/ui/components/SubagentManagement/index.js.map +1 -0
- package/dist/src/ui/components/SubagentManagement/types.d.ts +109 -0
- package/dist/src/ui/components/SubagentManagement/types.js +77 -0
- package/dist/src/ui/components/SubagentManagement/types.js.map +1 -0
- package/dist/src/ui/contexts/KeypressContext.js +2 -1
- package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
- package/dist/src/ui/contexts/RuntimeContext.d.ts +2 -1
- package/dist/src/ui/contexts/RuntimeContext.js +2 -1
- package/dist/src/ui/contexts/RuntimeContext.js.map +1 -1
- package/dist/src/ui/contexts/UIActionsContext.d.ts +13 -0
- package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
- package/dist/src/ui/contexts/UIStateContext.d.ts +21 -0
- package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.js +25 -1
- package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.test.js +127 -91
- package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.d.ts +5 -0
- package/dist/src/ui/hooks/slashCommandProcessor.js +34 -0
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useEditorSettings.test.js +3 -0
- package/dist/src/ui/hooks/useEditorSettings.test.js.map +1 -1
- package/dist/src/ui/hooks/useFolderTrust.js +1 -1
- package/dist/src/ui/hooks/useFolderTrust.js.map +1 -1
- package/dist/src/ui/hooks/useProfileManagement.d.ts +40 -0
- package/dist/src/ui/hooks/useProfileManagement.js +350 -0
- package/dist/src/ui/hooks/useProfileManagement.js.map +1 -0
- package/dist/src/ui/hooks/useReactToolScheduler.js +103 -33
- package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.test.js +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js +322 -220
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/layouts/DefaultAppLayout.js +5 -0
- package/dist/src/ui/layouts/DefaultAppLayout.js.map +1 -1
- package/dist/src/ui/reducers/appReducer.d.ts +5 -2
- package/dist/src/ui/reducers/appReducer.js +3 -0
- package/dist/src/ui/reducers/appReducer.js.map +1 -1
- package/dist/src/ui/reducers/appReducer.test.js +6 -0
- package/dist/src/ui/reducers/appReducer.test.js.map +1 -1
- package/dist/src/utils/sandbox.d.ts +1 -0
- package/dist/src/utils/sandbox.js +22 -20
- package/dist/src/utils/sandbox.js.map +1 -1
- package/dist/src/utils/sandbox.test.d.ts +6 -0
- package/dist/src/utils/sandbox.test.js +176 -0
- package/dist/src/utils/sandbox.test.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- 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
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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 @${
|
|
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
|
-
|
|
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 @${
|
|
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
|
-
|
|
171
|
-
const textBefore = '
|
|
172
|
-
const textAfter = '
|
|
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:
|
|
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 @${
|
|
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
|
-
|
|
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 @${
|
|
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
|
-
|
|
261
|
+
await createTestFile(path.join(testRootDir, relativePath1), content1);
|
|
227
262
|
const content2 = 'Content file2';
|
|
228
263
|
const relativePath2 = 'file2.md';
|
|
229
|
-
|
|
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 @${
|
|
278
|
+
{ text: `\nContent from @${relativePath1}:\n` },
|
|
244
279
|
{ text: content1 },
|
|
245
|
-
{ text: `\nContent from @${
|
|
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
|
-
|
|
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
|
-
|
|
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 @${
|
|
310
|
+
{ text: `\nContent from @${relativePath1}:\n` },
|
|
276
311
|
{ text: content1 },
|
|
277
|
-
{ text: `\nContent from @${
|
|
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
|
-
|
|
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
|
-
|
|
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 @${
|
|
347
|
+
expect(queryText).toContain(`Content from @${relativePath1}:`);
|
|
313
348
|
expect(queryText).toContain(content1);
|
|
314
|
-
expect(queryText).toContain(`Content from @${
|
|
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
|
-
|
|
345
|
-
const query =
|
|
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(
|
|
359
|
-
expect(mockOnDebugMessage).toHaveBeenCalledWith(
|
|
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
|
-
|
|
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 @${
|
|
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
|
-
|
|
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 @${
|
|
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
|
-
|
|
415
|
-
const query =
|
|
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(
|
|
429
|
-
expect(mockOnDebugMessage).toHaveBeenCalledWith(
|
|
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
|
-
|
|
456
|
-
const query =
|
|
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(
|
|
470
|
-
expect(mockOnDebugMessage).toHaveBeenCalledWith(
|
|
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
|
-
|
|
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 @${
|
|
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
|
-
|
|
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 @${
|
|
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
|
-
|
|
607
|
-
const query = queryTemplate(
|
|
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 @${
|
|
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
|
-
|
|
665
|
+
await createTestFile(path.join(testRootDir, 'first.txt'), content1);
|
|
630
666
|
const content2 = 'Second file';
|
|
631
|
-
|
|
632
|
-
const query =
|
|
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:
|
|
679
|
+
{ text: query },
|
|
644
680
|
{ text: '\n--- Content from referenced files ---' },
|
|
645
|
-
{ text:
|
|
681
|
+
{ text: '\nContent from @first.txt:\n' },
|
|
646
682
|
{ text: content1 },
|
|
647
|
-
{ text:
|
|
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
|
-
|
|
657
|
-
const escapedPath = path.join(
|
|
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:
|
|
705
|
+
{ text: 'Check @spaced file.txt, it has spaces.' },
|
|
670
706
|
{ text: '\n--- Content from referenced files ---' },
|
|
671
|
-
{ text:
|
|
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
|
-
|
|
681
|
-
const query =
|
|
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:
|
|
728
|
+
{ text: query },
|
|
693
729
|
{ text: '\n--- Content from referenced files ---' },
|
|
694
|
-
{ text:
|
|
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
|
-
|
|
704
|
-
const query =
|
|
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:
|
|
751
|
+
{ text: query },
|
|
716
752
|
{ text: '\n--- Content from referenced files ---' },
|
|
717
|
-
{ text:
|
|
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
|
-
|
|
727
|
-
const query =
|
|
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:
|
|
774
|
+
{ text: query },
|
|
739
775
|
{ text: '\n--- Content from referenced files ---' },
|
|
740
|
-
{ text:
|
|
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
|
-
|
|
750
|
-
const query =
|
|
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:
|
|
797
|
+
{ text: query },
|
|
762
798
|
{ text: '\n--- Content from referenced files ---' },
|
|
763
|
-
{ text:
|
|
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
|
-
|
|
773
|
-
const query =
|
|
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:
|
|
820
|
+
{ text: query },
|
|
785
821
|
{ text: '\n--- Content from referenced files ---' },
|
|
786
|
-
{ text:
|
|
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
|
-
|
|
796
|
-
const query =
|
|
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:
|
|
843
|
+
{ text: 'Check @file$with&special#chars.txt for content.' },
|
|
808
844
|
{ text: '\n--- Content from referenced files ---' },
|
|
809
|
-
{ text:
|
|
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
|
-
|
|
819
|
-
const query =
|
|
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:
|
|
866
|
+
{ text: query },
|
|
831
867
|
{ text: '\n--- Content from referenced files ---' },
|
|
832
|
-
{ text:
|
|
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
|
-
|
|
844
|
-
const query =
|
|
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,
|