Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -120,5 +120,6 @@ tmp/
# code graph skills
.venv
.codegraph
.zvec-grep/
.qwen/computer-use/installed.json
.playwright-mcp/
18 changes: 18 additions & 0 deletions packages/cli/src/config/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1761,6 +1761,24 @@ describe('mergeExcludeTools', () => {
expect(config.getPermissionsDeny()).toContain('tool_search');
});

it('should leave zvec_grep disabled by default', async () => {
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const settings: Settings = {};
const config = await loadCliConfig(settings, argv, undefined, []);
expect(config.isZvecGrepEnabled()).toBe(false);
});

it('should enable zvec_grep when tools.zvecGrep.enabled is true', async () => {
process.argv = ['node', 'script.js'];
const argv = await parseArguments();
const settings: Settings = {
tools: { zvecGrep: { enabled: true } },
};
const config = await loadCliConfig(settings, argv, undefined, []);
expect(config.isZvecGrepEnabled()).toBe(true);
});

it('should auto-disable tool_search for deepseek-v4 models', async () => {
process.argv = ['node', 'script.js', '--model', 'deepseek-v4-flash'];
const argv = await parseArguments();
Expand Down
9 changes: 5 additions & 4 deletions packages/cli/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1411,7 +1411,8 @@ export async function loadCliConfig(
): Promise<Config> {
const debugMode = isDebugMode(argv);
const bareMode = isBareMode(argv.bare);
const safeMode = argv.safeMode !== undefined ? argv.safeMode : isSafeModeEnv();
const safeMode =
argv.safeMode !== undefined ? argv.safeMode : isSafeModeEnv();

// Surface `--insecure` as an env var so it reaches the undici dispatcher
// layer (which controls TLS verification) without threading a flag through
Expand Down Expand Up @@ -1919,6 +1920,8 @@ export async function loadCliConfig(
disabledSlashCommands.length > 0 ? disabledSlashCommands : undefined,
disabledSkillNamesProvider:
bareMode || safeMode ? undefined : disabledSkillNamesProvider,
zvecGrepEnabled:
bareMode || safeMode ? false : settings.tools?.zvecGrep?.enabled === true,
disabledTools: disabledTools.length > 0 ? disabledTools : undefined,
// New unified permissions (PermissionManager source of truth).
permissions: {
Expand Down Expand Up @@ -2093,9 +2096,7 @@ export async function loadCliConfig(
? false
: (settings.memory?.enableTeamMemorySync ?? false),
enableAutoSkill:
bareMode || safeMode
? false
: (settings.memory?.enableAutoSkill ?? true),
bareMode || safeMode ? false : (settings.memory?.enableAutoSkill ?? true),
autoSkillConfirm:
bareMode || safeMode
? false
Expand Down
21 changes: 21 additions & 0 deletions packages/cli/src/config/settingsSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2102,6 +2102,27 @@ const SETTINGS_SCHEMA = {
},
},
},
zvecGrep: {
type: 'object',
label: 'Zvec Grep',
category: 'Tools',
requiresRestart: true,
default: {},
description: 'Settings for the zvec-grep built-in search tool.',
showInDialog: false,
properties: {
enabled: {
type: 'boolean',
label: 'Enable Zvec Grep',
category: 'Tools',
requiresRestart: true,
default: false,
description:
'When enabled, registers the zvec_grep built-in tool. Disabled by default.',
showInDialog: false,
},
},
},
shell: {
type: 'object',
label: 'Shell',
Expand Down
28 changes: 28 additions & 0 deletions packages/core/src/config/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5166,6 +5166,34 @@ describe('setApprovalMode with folder trust', () => {
vi.clearAllMocks();
});

it('should not register zvec-grep tool by default', async () => {
const config = new Config({ ...baseParams, useRipgrep: false });
await config.initialize();

const calls = (ToolRegistry.prototype.registerFactory as Mock).mock.calls;
const zvecGrepRegistrations = calls.filter(
(call) => call[0] === ToolNames.ZVEC_GREP,
);

expect(zvecGrepRegistrations.length).toBe(0);
});

it('should register zvec-grep tool when enabled', async () => {
const config = new Config({
...baseParams,
useRipgrep: false,
zvecGrepEnabled: true,
});
await config.initialize();

const calls = (ToolRegistry.prototype.registerFactory as Mock).mock.calls;
const zvecGrepRegistrations = calls.filter(
(call) => call[0] === ToolNames.ZVEC_GREP,
);

expect(zvecGrepRegistrations.length).toBe(1);
});

it('should register grep tool when useRipgrep is true and it is available', async () => {
(canUseRipgrep as Mock).mockResolvedValue(true);
const config = new Config({ ...baseParams, useRipgrep: true });
Expand Down
14 changes: 14 additions & 0 deletions packages/core/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,7 @@ export interface ConfigParameters {
* Names returned must be lower-cased; consumers compare case-insensitively.
*/
disabledSkillNamesProvider?: () => ReadonlySet<string>;
zvecGrepEnabled?: boolean;
/**
* Tool names hidden from the registry at construction time. Unlike
* `permissions.deny` (which keeps the tool registered and rejects
Expand Down Expand Up @@ -1348,6 +1349,7 @@ export class Config {
private readonly disabledSkillNamesProvider:
| (() => ReadonlySet<string>)
| null;
private readonly zvecGrepEnabled: boolean;
// `disabledTools` is set at construction
// time but can be re-synced by the daemon mutation surface
// (`setWorkspaceToolEnabled` propagates through ACP) so a subsequent
Expand Down Expand Up @@ -1595,6 +1597,7 @@ export class Config {
...(params.disabledSlashCommands ?? []),
]);
this.disabledSkillNamesProvider = params.disabledSkillNamesProvider ?? null;
this.zvecGrepEnabled = params.zvecGrepEnabled ?? false;
this.disabledTools = new Set(params.disabledTools ?? []);
this.permissionsAllow = params.permissions?.allow || [];
this.permissionsAsk = params.permissions?.ask || [];
Expand Down Expand Up @@ -3668,6 +3671,10 @@ export class Config {
return this.disabledSkillNamesProvider?.() ?? EMPTY_DISABLED_SKILL_NAMES;
}

isZvecGrepEnabled(): boolean {
return this.zvecGrepEnabled;
}

/**
* Returns the read-only set of tool names hidden from this Config's
* ToolRegistry. Consulted by `ToolRegistry.registerTool` and
Expand Down Expand Up @@ -5767,6 +5774,13 @@ export class Config {
return new ReadFileTool(this);
});

if (this.isZvecGrepEnabled()) {
await registerLazy(ToolNames.ZVEC_GREP, async () => {
const { ZvecGrepTool } = await import('../tools/zvec-grep.js');
return new ZvecGrepTool(this);
});
}

// --- Grep / RipGrep (conditional) ---
if (this.getUseRipgrep()) {
let useRipgrep = false;
Expand Down
12 changes: 12 additions & 0 deletions packages/core/src/core/coreToolScheduler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10292,6 +10292,18 @@ describe('extractToolFilePaths', () => {
).toEqual(['packages/core', 'packages/core/**/*.ts']);
});

it('extracts zvec_grep paths and globs as path-shaped file filters', () => {
expect(
extractToolFilePaths('zvec_grep', {
operation: 'rg',
query: 'validate',
paths: ['src', 'include'],
glob: '**/*.{h,cc}',
exclude: ['thirdparty/**'],
}),
).toEqual(['src', 'include', '**/*.{h,cc}', 'thirdparty/**']);
});

it('decodes file:// URIs for lsp via fileURLToPath', () => {
// Regression: LSP `filePath` is allowed to be a `file://` URI.
// Forwarding the URI as-is to the activation registry would never
Expand Down
19 changes: 19 additions & 0 deletions packages/core/src/core/coreToolScheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ const FS_PATH_TOOL_NAMES: ReadonlySet<string> = new Set<string>([
ToolNames.EDIT,
ToolNames.WRITE_FILE,
ToolNames.GREP,
ToolNames.ZVEC_GREP,
ToolNames.GLOB,
ToolNames.LS,
ToolNames.LSP,
Expand Down Expand Up @@ -620,6 +621,24 @@ export function extractToolFilePaths(
return out;
}

case ToolNames.ZVEC_GREP: {
const pathsField = obj['paths'];
const globField = obj['glob'];
const excludeField = obj['exclude'];
if (Array.isArray(pathsField)) {
for (const item of pathsField) {
push(item);
}
}
push(globField);
if (Array.isArray(excludeField)) {
for (const item of excludeField) {
push(item);
}
}
return out;
}

case ToolNames.LS:
push(obj['path']);
return out;
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/tools/tool-names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const ToolNames = {
WRITE_FILE: 'write_file',
READ_FILE: 'read_file',
GREP: 'grep_search',
ZVEC_GREP: 'zvec_grep',
GLOB: 'glob',
SHELL: 'run_shell_command',
TODO_WRITE: 'todo_write',
Expand Down Expand Up @@ -72,6 +73,7 @@ export const ToolDisplayNames = {
WRITE_FILE: 'WriteFile',
READ_FILE: 'ReadFile',
GREP: 'Grep',
ZVEC_GREP: 'ZvecGrep',
GLOB: 'Glob',
SHELL: 'Shell',
TODO_WRITE: 'TodoList',
Expand Down
Loading
Loading