fix(tools): Handle special characters in file paths for glob and read_many_files (#6507)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
This commit is contained in:
parent
fb3ceb0da4
commit
6fc68ff8d4
|
@ -150,6 +150,34 @@ describe('GlobTool', () => {
|
||||||
expect(result.returnDisplay).toBe('No files found');
|
expect(result.returnDisplay).toBe('No files found');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should find files with special characters in the name', async () => {
|
||||||
|
await fs.writeFile(path.join(tempRootDir, 'file[1].txt'), 'content');
|
||||||
|
const params: GlobToolParams = { pattern: 'file[1].txt' };
|
||||||
|
const invocation = globTool.build(params);
|
||||||
|
const result = await invocation.execute(abortSignal);
|
||||||
|
expect(result.llmContent).toContain('Found 1 file(s)');
|
||||||
|
expect(result.llmContent).toContain(
|
||||||
|
path.join(tempRootDir, 'file[1].txt'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find files with special characters like [] and () in the path', async () => {
|
||||||
|
const filePath = path.join(
|
||||||
|
tempRootDir,
|
||||||
|
'src/app/[test]/(dashboard)/testing/components/code.tsx',
|
||||||
|
);
|
||||||
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
||||||
|
await fs.writeFile(filePath, 'content');
|
||||||
|
|
||||||
|
const params: GlobToolParams = {
|
||||||
|
pattern: 'src/app/[test]/(dashboard)/testing/components/code.tsx',
|
||||||
|
};
|
||||||
|
const invocation = globTool.build(params);
|
||||||
|
const result = await invocation.execute(abortSignal);
|
||||||
|
expect(result.llmContent).toContain('Found 1 file(s)');
|
||||||
|
expect(result.llmContent).toContain(filePath);
|
||||||
|
});
|
||||||
|
|
||||||
it('should correctly sort files by modification time (newest first)', async () => {
|
it('should correctly sort files by modification time (newest first)', async () => {
|
||||||
const params: GlobToolParams = { pattern: '*.sortme' };
|
const params: GlobToolParams = { pattern: '*.sortme' };
|
||||||
const invocation = globTool.build(params);
|
const invocation = globTool.build(params);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { glob } from 'glob';
|
import { glob, escape } from 'glob';
|
||||||
import { SchemaValidator } from '../utils/schemaValidator.js';
|
import { SchemaValidator } from '../utils/schemaValidator.js';
|
||||||
import {
|
import {
|
||||||
BaseDeclarativeTool,
|
BaseDeclarativeTool,
|
||||||
|
@ -137,7 +137,13 @@ class GlobToolInvocation extends BaseToolInvocation<
|
||||||
let allEntries: GlobPath[] = [];
|
let allEntries: GlobPath[] = [];
|
||||||
|
|
||||||
for (const searchDir of searchDirectories) {
|
for (const searchDir of searchDirectories) {
|
||||||
const entries = (await glob(this.params.pattern, {
|
let pattern = this.params.pattern;
|
||||||
|
const fullPath = path.join(searchDir, pattern);
|
||||||
|
if (fs.existsSync(fullPath)) {
|
||||||
|
pattern = escape(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
const entries = (await glob(pattern, {
|
||||||
cwd: searchDir,
|
cwd: searchDir,
|
||||||
withFileTypes: true,
|
withFileTypes: true,
|
||||||
nodir: true,
|
nodir: true,
|
||||||
|
|
|
@ -527,6 +527,43 @@ describe('ReadManyFilesTool', () => {
|
||||||
expect(truncatedFileContent).toContain('L200');
|
expect(truncatedFileContent).toContain('L200');
|
||||||
expect(truncatedFileContent).not.toContain('L2400');
|
expect(truncatedFileContent).not.toContain('L2400');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should read files with special characters like [] and () in the path', async () => {
|
||||||
|
const filePath = 'src/app/[test]/(dashboard)/testing/components/code.tsx';
|
||||||
|
createFile(filePath, 'Content of receive-detail');
|
||||||
|
const params = { paths: [filePath] };
|
||||||
|
const invocation = tool.build(params);
|
||||||
|
const result = await invocation.execute(new AbortController().signal);
|
||||||
|
const expectedPath = path.join(tempRootDir, filePath);
|
||||||
|
expect(result.llmContent).toEqual([
|
||||||
|
`--- ${expectedPath} ---
|
||||||
|
|
||||||
|
Content of receive-detail
|
||||||
|
|
||||||
|
`,
|
||||||
|
]);
|
||||||
|
expect(result.returnDisplay).toContain(
|
||||||
|
'Successfully read and concatenated content from **1 file(s)**',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should read files with special characters in the name', async () => {
|
||||||
|
createFile('file[1].txt', 'Content of file[1]');
|
||||||
|
const params = { paths: ['file[1].txt'] };
|
||||||
|
const invocation = tool.build(params);
|
||||||
|
const result = await invocation.execute(new AbortController().signal);
|
||||||
|
const expectedPath = path.join(tempRootDir, 'file[1].txt');
|
||||||
|
expect(result.llmContent).toEqual([
|
||||||
|
`--- ${expectedPath} ---
|
||||||
|
|
||||||
|
Content of file[1]
|
||||||
|
|
||||||
|
`,
|
||||||
|
]);
|
||||||
|
expect(result.returnDisplay).toContain(
|
||||||
|
'Successfully read and concatenated content from **1 file(s)**',
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Batch Processing', () => {
|
describe('Batch Processing', () => {
|
||||||
|
|
|
@ -13,8 +13,9 @@ import {
|
||||||
} from './tools.js';
|
} from './tools.js';
|
||||||
import { SchemaValidator } from '../utils/schemaValidator.js';
|
import { SchemaValidator } from '../utils/schemaValidator.js';
|
||||||
import { getErrorMessage } from '../utils/errors.js';
|
import { getErrorMessage } from '../utils/errors.js';
|
||||||
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { glob } from 'glob';
|
import { glob, escape } from 'glob';
|
||||||
import { getCurrentGeminiMdFilename } from './memoryTool.js';
|
import { getCurrentGeminiMdFilename } from './memoryTool.js';
|
||||||
import {
|
import {
|
||||||
detectFileType,
|
detectFileType,
|
||||||
|
@ -245,18 +246,27 @@ ${finalExclusionPatternsForDescription
|
||||||
const workspaceDirs = this.config.getWorkspaceContext().getDirectories();
|
const workspaceDirs = this.config.getWorkspaceContext().getDirectories();
|
||||||
|
|
||||||
for (const dir of workspaceDirs) {
|
for (const dir of workspaceDirs) {
|
||||||
const entriesInDir = await glob(
|
const processedPatterns = [];
|
||||||
searchPatterns.map((p) => p.replace(/\\/g, '/')),
|
for (const p of searchPatterns) {
|
||||||
{
|
const normalizedP = p.replace(/\\/g, '/');
|
||||||
cwd: dir,
|
const fullPath = path.join(dir, normalizedP);
|
||||||
ignore: effectiveExcludes,
|
if (fs.existsSync(fullPath)) {
|
||||||
nodir: true,
|
processedPatterns.push(escape(normalizedP));
|
||||||
dot: true,
|
} else {
|
||||||
absolute: true,
|
// The path does not exist or is not a file, so we treat it as a glob pattern.
|
||||||
nocase: true,
|
processedPatterns.push(normalizedP);
|
||||||
signal,
|
}
|
||||||
},
|
}
|
||||||
);
|
|
||||||
|
const entriesInDir = await glob(processedPatterns, {
|
||||||
|
cwd: dir,
|
||||||
|
ignore: effectiveExcludes,
|
||||||
|
nodir: true,
|
||||||
|
dot: true,
|
||||||
|
absolute: true,
|
||||||
|
nocase: true,
|
||||||
|
signal,
|
||||||
|
});
|
||||||
for (const entry of entriesInDir) {
|
for (const entry of entriesInDir) {
|
||||||
allEntries.add(entry);
|
allEntries.add(entry);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue