Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/config-follow-gitignore.md
Original file line number Diff line number Diff line change
@@ -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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reduce the changeset to one user-facing sentence

This changeset contains two sentences: one describing the new config setting and another describing the per-call override. Repository workflow requires changesets to contain exactly one short user-facing sentence stating only what changed, so combine or remove the second sentence before merging.

AGENTS.md reference: AGENTS.md:L86-L88

Useful? React with 👍 / 👎.

5 changes: 5 additions & 0 deletions packages/agent-core-v2/src/agent/toolPolicy/configSection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Comment on lines +10 to +14

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Regenerate the config manifest for the new search setting

When packages/agent-core-v2/test/app/config/configManifest.test.ts builds the manifest, it derives the nested search.follow_gitignore field from this schema, but the committed packages/agent-core-v2/docs/config-manifest.toml still ends the [tools] section after disabled. Its equality assertion will therefore fail until the generated manifest is updated.

Useful? React with 👍 / 👎.

});

export type ToolsConfig = z.infer<typeof ToolsConfigSchema>;
Expand Down
2 changes: 1 addition & 1 deletion packages/agent-core-v2/src/agent/tools/os/glob/glob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
14 changes: 11 additions & 3 deletions packages/agent-core-v2/src/agent/tools/os/glob/globTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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}`,
Expand All @@ -130,7 +138,7 @@ export class GlobTool implements IGlobTool {
lease.runtime.process!,
env,
workspace,
args,
executionArgs,
signal,
searchRoots,
);
Expand Down
2 changes: 1 addition & 1 deletion packages/agent-core-v2/src/agent/tools/os/grep/grep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.',
),
});

Expand Down
13 changes: 11 additions & 2 deletions packages/agent-core-v2/src/agent/tools/os/grep/grepTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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}`,
Expand All @@ -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();
}
Expand Down