diff --git a/.changeset/config-follow-gitignore.md b/.changeset/config-follow-gitignore.md new file mode 100644 index 00000000000..6c75419d52e --- /dev/null +++ b/.changeset/config-follow-gitignore.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": minor +--- + +Add `tools.search.follow_gitignore = false` to config.toml to make Glob and Grep search gitignored files (such as build outputs) by default. The existing `include_ignored` parameter on each tool still works as a per-call override. diff --git a/packages/agent-core-v2/src/agent/toolPolicy/configSection.ts b/packages/agent-core-v2/src/agent/toolPolicy/configSection.ts index 61df6360db5..d419fca2ad6 100644 --- a/packages/agent-core-v2/src/agent/toolPolicy/configSection.ts +++ b/packages/agent-core-v2/src/agent/toolPolicy/configSection.ts @@ -7,6 +7,11 @@ export const TOOLS_SECTION = 'tools'; export const ToolsConfigSchema = z.object({ enabled: z.array(z.string()).optional(), disabled: z.array(z.string()).optional(), + search: z + .object({ + follow_gitignore: z.boolean().default(true), + }) + .optional(), }); export type ToolsConfig = z.infer; diff --git a/packages/agent-core-v2/src/agent/tools/os/glob/glob.ts b/packages/agent-core-v2/src/agent/tools/os/glob/glob.ts index 5043a2e68bf..f31ae979bfb 100644 --- a/packages/agent-core-v2/src/agent/tools/os/glob/glob.ts +++ b/packages/agent-core-v2/src/agent/tools/os/glob/glob.ts @@ -15,7 +15,7 @@ export const GlobInputSchema = z.object({ .boolean() .optional() .describe( - 'Also match files excluded by ignore files such as `.gitignore`, `.ignore`, and `.rgignore` (for example `node_modules` or build outputs). Sensitive files (such as `.env`) remain filtered out for safety. VCS metadata directories (`.git` and similar) are always skipped, even when this is true. Defaults to false.', + 'Also match files excluded by ignore files such as `.gitignore`, `.ignore`, and `.rgignore` (for example `node_modules` or build outputs). Sensitive files (such as `.env`) remain filtered out for safety. VCS metadata directories (`.git` and similar) are always skipped, even when this is true. Defaults to false unless tools.search.follow_gitignore is set to false in config.toml.', ), include_dirs: z .boolean() diff --git a/packages/agent-core-v2/src/agent/tools/os/glob/globTool.ts b/packages/agent-core-v2/src/agent/tools/os/glob/globTool.ts index 75110f802bb..3952d78307c 100644 --- a/packages/agent-core-v2/src/agent/tools/os/glob/globTool.ts +++ b/packages/agent-core-v2/src/agent/tools/os/glob/globTool.ts @@ -13,6 +13,7 @@ import type { IHostProcessService } from '#/os/interface/hostProcess'; import { IAgentRuntimeService, inspectAgentRuntime } from '#/agent/runtimeBinding/agentRuntime'; import { unwrapErrorCause } from '#/_base/errors/errors'; import { RuntimeWorkspaceView } from '#/runtime/runtimeWorkspaceView'; +import { IConfigService } from '#/app/config/config'; import { ISessionSkillCatalog } from '#/features/skill/session/skillCatalog'; import { ISessionWorkspaceContext } from '#/session/workspaceContext/workspaceContext'; import { ITelemetryService } from '#/app/telemetry/telemetry'; @@ -30,6 +31,7 @@ import { SENSITIVE_DOT_VARIANT_SUFFIXES, type WorkspaceConfig, } from '#/tool/path-access'; +import { TOOLS_SECTION } from '#/agent/toolPolicy/configSection'; import { toInputJsonSchema } from '#/tool/input-schema'; import { literalRulePattern, matchesGlobRuleSubject } from '#/tool/rule-match'; import globDescription from './glob.md?raw'; @@ -65,7 +67,8 @@ export class GlobTool implements IGlobTool { @IAgentRuntimeService private readonly runtime: IAgentRuntimeService, @ISessionWorkspaceContext private readonly workspaceCtx: ISessionWorkspaceContext, @ITelemetryService private readonly telemetry: ITelemetryService, - @ISessionSkillCatalog private readonly skillCatalog?: ISessionSkillCatalog, + @ISessionSkillCatalog private readonly skillCatalog: ISessionSkillCatalog | undefined, + @IConfigService private readonly config: IConfigService, ) {} get description(): string { @@ -100,14 +103,19 @@ export class GlobTool implements IGlobTool { } const searchRoots = [path ?? workspace.workspaceDir]; + const effectiveIncludeIgnored = + args.include_ignored ?? this.config.get<{ search?: { follow_gitignore?: boolean } }>(TOOLS_SECTION)?.search?.follow_gitignore === false; + const detailParts: string[] = [`pattern: ${args.pattern}`]; if (args.path !== undefined) { detailParts.push(`path: ${args.path}`); } - if (args.include_ignored === true) { + if (effectiveIncludeIgnored) { detailParts.push('include_ignored: true'); } + const executionArgs = { ...args, include_ignored: effectiveIncludeIgnored }; + return { accesses: ToolAccesses.searchTree(searchRoots[0]!), description: `Searching ${args.pattern}`, @@ -130,7 +138,7 @@ export class GlobTool implements IGlobTool { lease.runtime.process!, env, workspace, - args, + executionArgs, signal, searchRoots, ); diff --git a/packages/agent-core-v2/src/agent/tools/os/grep/grep.ts b/packages/agent-core-v2/src/agent/tools/os/grep/grep.ts index 3941bb12d10..8e04fd37b50 100644 --- a/packages/agent-core-v2/src/agent/tools/os/grep/grep.ts +++ b/packages/agent-core-v2/src/agent/tools/os/grep/grep.ts @@ -86,7 +86,7 @@ export const GrepInputSchema = z.object({ .boolean() .optional() .describe( - 'Also search files excluded by ignore files such as `.gitignore`, `.ignore`, and `.rgignore` (for example `node_modules` or build outputs). Sensitive files (such as `.env`) remain filtered out for safety. VCS metadata directories (`.git` and similar) are always skipped, even when this is true. Defaults to false.', + 'Also search files excluded by ignore files such as `.gitignore`, `.ignore`, and `.rgignore` (for example `node_modules` or build outputs). Sensitive files (such as `.env`) remain filtered out for safety. VCS metadata directories (`.git` and similar) are always skipped, even when this is true. Defaults to false unless tools.search.follow_gitignore is set to false in config.toml.', ), }); diff --git a/packages/agent-core-v2/src/agent/tools/os/grep/grepTool.ts b/packages/agent-core-v2/src/agent/tools/os/grep/grepTool.ts index 174817c6e27..6822f384064 100644 --- a/packages/agent-core-v2/src/agent/tools/os/grep/grepTool.ts +++ b/packages/agent-core-v2/src/agent/tools/os/grep/grepTool.ts @@ -14,6 +14,7 @@ import type { IHostProcessService } from '#/os/interface/hostProcess'; import { IAgentRuntimeService, inspectAgentRuntime } from '#/agent/runtimeBinding/agentRuntime'; import { RuntimeWorkspaceView } from '#/runtime/runtimeWorkspaceView'; import { unwrapErrorCause } from '#/_base/errors/errors'; +import { IConfigService } from '#/app/config/config'; import { ISessionSkillCatalog } from '#/features/skill/session/skillCatalog'; import { ISessionWorkspaceContext } from '#/session/workspaceContext/workspaceContext'; import { @@ -23,6 +24,7 @@ import { SENSITIVE_DOT_VARIANT_SUFFIXES, type WorkspaceConfig, } from '#/tool/path-access'; +import { TOOLS_SECTION } from '#/agent/toolPolicy/configSection'; import { toInputJsonSchema } from '#/tool/input-schema'; import { literalRulePattern, matchesGlobRuleSubject } from '#/tool/rule-match'; import { @@ -71,7 +73,8 @@ export class GrepTool implements IGrepTool { @IAgentRuntimeService private readonly runtime: IAgentRuntimeService, @ISessionWorkspaceContext private readonly workspaceCtx: ISessionWorkspaceContext, @ITelemetryService private readonly telemetry: ITelemetryService, - @ISessionSkillCatalog private readonly skillCatalog?: ISessionSkillCatalog, + @ISessionSkillCatalog private readonly skillCatalog: ISessionSkillCatalog | undefined, + @IConfigService private readonly config: IConfigService, ) {} private workspace(view: RuntimeWorkspaceView): WorkspaceConfig { @@ -100,6 +103,12 @@ export class GrepTool implements IGrepTool { } const searchPaths = [path ?? workspace.workspaceDir]; const searchPath = args.path ?? workspace.workspaceDir; + + const effectiveIncludeIgnored = + args.include_ignored ?? this.config.get<{ search?: { follow_gitignore?: boolean } }>(TOOLS_SECTION)?.search?.follow_gitignore === false; + + const executionArgs = { ...args, include_ignored: effectiveIncludeIgnored }; + return { accesses: ToolAccesses.searchTree(searchPaths[0]!), description: `Searching for '${args.pattern}' in ${searchPath}`, @@ -112,7 +121,7 @@ export class GrepTool implements IGrepTool { if (lease.runtime.identity.generation !== inspected.identity.generation) { return { isError: true, output: 'Runtime changed before execution. Retry the tool call.' }; } - return await this.execution(lease.runtime.process!, lease.runtime.fs!, env, workspace, args, signal, searchPaths); + return await this.execution(lease.runtime.process!, lease.runtime.fs!, env, workspace, executionArgs, signal, searchPaths); } finally { lease.dispose(); }