diff --git a/packages/core/src/tools/glob.test.ts b/packages/core/src/tools/glob.test.ts index e747a1eec8..30a932d49e 100644 --- a/packages/core/src/tools/glob.test.ts +++ b/packages/core/src/tools/glob.test.ts @@ -659,6 +659,25 @@ describe('GlobTool', () => { expect(result.llmContent).toContain('[5 files truncated] ...'); }); }); + + describe('getDefaultPermission', () => { + it('should return allow for paths within workspace', async () => { + const params: GlobToolParams = { pattern: '*', path: 'sub' }; + const invocation = globTool.build(params); + const permission = await invocation.getDefaultPermission(); + expect(permission).toBe('allow'); + }); + + it('should return ask for tilde paths outside workspace', async () => { + const params: GlobToolParams = { + pattern: '*', + path: '~/outside-workspace', + }; + const invocation = globTool.build(params); + const permission = await invocation.getDefaultPermission(); + expect(permission).toBe('ask'); + }); + }); }); describe('sortFileEntries', () => { diff --git a/packages/core/src/tools/glob.ts b/packages/core/src/tools/glob.ts index 24cb661f8a..d68ecca59f 100644 --- a/packages/core/src/tools/glob.ts +++ b/packages/core/src/tools/glob.ts @@ -12,6 +12,7 @@ import { BaseDeclarativeTool, BaseToolInvocation, Kind } from './tools.js'; import { ToolNames, ToolDisplayNames } from './tool-names.js'; import { resolveAndValidatePath, + resolvePath, isSubpath, unescapePath, } from '../utils/paths.js'; @@ -114,7 +115,7 @@ class GlobToolInvocation extends BaseToolInvocation< return 'allow'; // Default workspace directory } const workspaceContext = this.config.getWorkspaceContext(); - const resolvedPath = path.resolve( + const resolvedPath = resolvePath( this.config.getTargetDir(), this.params.path, ); diff --git a/packages/core/src/tools/grep.test.ts b/packages/core/src/tools/grep.test.ts index 473efecf80..e3fcbe8419 100644 --- a/packages/core/src/tools/grep.test.ts +++ b/packages/core/src/tools/grep.test.ts @@ -597,6 +597,25 @@ describe('GrepTool', () => { }); }); + describe('getDefaultPermission', () => { + it('should return allow for paths within workspace', async () => { + const params: GrepToolParams = { pattern: 'hello', path: 'sub' }; + const invocation = grepTool.build(params); + const permission = await invocation.getDefaultPermission(); + expect(permission).toBe('allow'); + }); + + it('should return ask for tilde paths outside workspace', async () => { + const params: GrepToolParams = { + pattern: 'hello', + path: '~/outside-workspace', + }; + const invocation = grepTool.build(params); + const permission = await invocation.getDefaultPermission(); + expect(permission).toBe('ask'); + }); + }); + describe('Result limiting', () => { beforeEach(async () => { // Create many test files with matches to test limiting diff --git a/packages/core/src/tools/grep.ts b/packages/core/src/tools/grep.ts index ae1fa2edea..d0d30712f5 100644 --- a/packages/core/src/tools/grep.ts +++ b/packages/core/src/tools/grep.ts @@ -15,6 +15,7 @@ import { ToolNames, ToolDisplayNames } from './tool-names.js'; import { createDebugLogger } from '../utils/debugLogger.js'; import { resolveAndValidatePath, + resolvePath, isSubpath, unescapePath, } from '../utils/paths.js'; @@ -91,7 +92,7 @@ class GrepToolInvocation extends BaseToolInvocation< return 'allow'; // Default workspace directory } const workspaceContext = this.config.getWorkspaceContext(); - const resolvedPath = path.resolve( + const resolvedPath = resolvePath( this.config.getTargetDir(), this.params.path, ); diff --git a/packages/core/src/tools/ripGrep.test.ts b/packages/core/src/tools/ripGrep.test.ts index ab4e4efe55..57b12e067a 100644 --- a/packages/core/src/tools/ripGrep.test.ts +++ b/packages/core/src/tools/ripGrep.test.ts @@ -1132,5 +1132,15 @@ describe('RipGrepTool', () => { const permission = await invocation.getDefaultPermission(); expect(permission).toBe('ask'); }); + + it('should return ask for tilde paths outside workspace', async () => { + const params: RipGrepToolParams = { + pattern: 'hello', + path: '~/outside-workspace', + }; + const invocation = grepTool.build(params); + const permission = await invocation.getDefaultPermission(); + expect(permission).toBe('ask'); + }); }); }); diff --git a/packages/core/src/tools/ripGrep.ts b/packages/core/src/tools/ripGrep.ts index 5faa6f95c7..34299d3b35 100644 --- a/packages/core/src/tools/ripGrep.ts +++ b/packages/core/src/tools/ripGrep.ts @@ -9,7 +9,11 @@ import path from 'node:path'; import type { ToolInvocation, ToolResult } from './tools.js'; import { BaseDeclarativeTool, BaseToolInvocation, Kind } from './tools.js'; import { ToolNames } from './tool-names.js'; -import { resolveAndValidatePath, unescapePath } from '../utils/paths.js'; +import { + resolveAndValidatePath, + resolvePath, + unescapePath, +} from '../utils/paths.js'; import { getErrorMessage } from '../utils/errors.js'; import type { Config } from '../config/config.js'; import { runRipgrep } from '../utils/ripgrepUtils.js'; @@ -149,7 +153,7 @@ class GrepToolInvocation extends BaseToolInvocation< return 'allow'; // Default workspace directory } const workspaceContext = this.config.getWorkspaceContext(); - const resolvedPath = path.resolve( + const resolvedPath = resolvePath( this.config.getTargetDir(), this.params.path, );