refactor: replace ListVaultFiles with content-based SearchVaultFiles

Replace list_vault_files function with search_vault_files that performs regex-based content search instead of listing all files. Add escapeRegex helper for search term sanitization. Remove unused edit mode CSS.
This commit is contained in:
Andrew Beal 2025-10-14 18:54:16 +01:00
parent 92d3bc5a31
commit f2a7a41705
9 changed files with 64 additions and 34 deletions

View file

@ -1,11 +1,11 @@
import type { IAIFunctionDefinition } from "./IAIFunctionDefinition";
import { ListVaultFiles } from "./Functions/ListVaultFiles";
import { SearchVaultFiles } from "./Functions/SearchVaultFiles";
import { ReadFile } from "./Functions/ReadFile";
export class AIFunctionDefinitions {
public getQueryActions(destructive: boolean): IAIFunctionDefinition[] {
const actions = [
ListVaultFiles,
SearchVaultFiles,
ReadFile
];

View file

@ -1,20 +0,0 @@
import { AIFunction } from "Enums/AIFunction";
import type { IAIFunctionDefinition } from "../IAIFunctionDefinition";
export const ListVaultFiles: IAIFunctionDefinition = {
name: AIFunction.ListVaultFiles,
description: `Returns complete list of vault files with metadata (names, paths, sizes).
Call this whenever you need to know what files exist in the vault to answer questions,
verify file presence, or to perform further agentic functions. Use proactively
when vault contents would inform your response.`,
parameters: {
type: "object",
properties: {
user_message: {
type: "string",
description: "A short message to be displayed to the user that explains the action being taken"
}
},
required: ["user_message"]
}
}

View file

@ -0,0 +1,26 @@
import { AIFunction } from "Enums/AIFunction";
import type { IAIFunctionDefinition } from "../IAIFunctionDefinition";
export const SearchVaultFiles: IAIFunctionDefinition = {
name: AIFunction.SearchVaultFiles,
description: `Searches through all files in the user's Obsidian vault for the given search term.
Uses regex pattern matching to search both file names and file content.
Returns list of matching vault files with metadata (names, paths) and contextual snippets showing matched content with surrounding text.
Call this whenever you need to know what files exist in the vault to answer questions,
verify file presence, or to perform further agentic functions.
Use proactively when vault contents would inform your response.`,
parameters: {
type: "object",
properties: {
search_term: {
type: "string",
description: "The regex pattern to search for in vault files. Supports both simple text (e.g., 'todo') and regex patterns (e.g., '(urgent|important)'). The search is performed on both file names and content."
},
user_message: {
type: "string",
description: "A short message to be displayed to the user that explains the action being taken"
}
},
required: ["search_term", "user_message"]
}
}

View file

@ -300,11 +300,6 @@
transition-duration: 0.5s;
}
#edit-mode-button.edit-mode {
box-shadow: 0px 0px 1px 1px var(--color-blue);
color: var(--color-blue);
}
#submit-button:not(:disabled):hover {
cursor: pointer;
background-color: var(--interactive-accent-hover);

View file

@ -1,5 +1,5 @@
export enum AIFunction {
ListVaultFiles = "list_vault_files",
SearchVaultFiles = "search_vault_files",
ReadFile = "read_file",
// used by gemini

View file

@ -33,4 +33,8 @@ export function dateToString(date: Date, includeTime: boolean = true): string {
day: '2-digit'
}).replace(/[:\s]/g, '-');
}
}
export function escapeRegex(string: string): string {
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

View file

@ -12,8 +12,8 @@ export class AIFunctionService {
public async performAIFunction(functionCall: AIFunctionCall): Promise<AIFunctionResponse> {
switch (functionCall.name) {
case AIFunction.ListVaultFiles:
return new AIFunctionResponse(functionCall.name, await this.listVaultFiles());
case AIFunction.SearchVaultFiles:
return new AIFunctionResponse(functionCall.name, await this.searchVaultFiles(functionCall.arguments.search_term));
case AIFunction.ReadFile:
return new AIFunctionResponse(functionCall.name, await this.readFile(functionCall.arguments.file_path));
@ -32,12 +32,11 @@ export class AIFunctionService {
}
}
private async listVaultFiles(): Promise<object> {
const files: TFile[] = await this.fileSystemService.listFilesInDirectory("/");
private async searchVaultFiles(searchTerm: string): Promise<object> {
const files: TFile[] = await this.fileSystemService.searchVaultFiles(searchTerm);
return files.map((file) => ({
name: file.basename,
path: file.path,
size_bytes: file.stat.size
path: file.path
}));
}

View file

@ -96,6 +96,10 @@ export class FileSystemService {
}
}
public async searchVaultFiles(searchTerm: string): Promise<TFile[]> {
return await this.vaultService.searchVaultFiles(searchTerm);
}
private async createDirectories(vaultService: VaultService, filePath: string, allowAccessToPluginRoot: boolean = false) {
const dirPath: string = filePath.substring(0, filePath.lastIndexOf('/'));

View file

@ -3,6 +3,7 @@ import { Resolve } from "./DependencyService";
import { Services } from "./Services";
import type AIAgentPlugin from "main";
import { Path } from "Enums/Path";
import { escapeRegex } from "Helpers/Helpers";
/* This service protects the users vault through their exclusions. The plugin root is excluded by default */
export class VaultService {
@ -90,6 +91,27 @@ export class VaultService {
return files.filter(file => !this.isExclusion(file.path, allowAccessToPluginRoot));
}
public async searchVaultFiles(searchTerm: string): Promise<TFile[]> {
let regex: RegExp;
try {
regex = new RegExp(searchTerm, "i");
} catch {
regex = new RegExp(escapeRegex(searchTerm), "i");
}
const files: TFile[] = this.vault.getFiles().filter(file => !this.isExclusion(file.path));
const matches: TFile[] = [];
for (const file of files) {
const content = await this.vault.cachedRead(file);
if (regex.test(content)) {
matches.push(file);
}
}
return matches;
}
private isExclusion(filePath: string, allowAccessToPluginRoot: boolean = false): boolean {
// the ai should never be able to edit the user instruction
const exclusions = allowAccessToPluginRoot