// // SearchMyDataTool.swift // Poche // // Author: Simon-Pierre Boucher // Contact: contact@spboucher.ai // import Foundation import FoundationModels /// Read-only. Long-term memory lives in SwiftData and is re-injected on /// demand through this tool, never kept permanently in the context /// (CLAUDE.md §5). struct SearchMyDataTool: Tool { let name = "searchMyData" let description = "Recherche dans les notes, tâches et souvenirs enregistrés dans Poche." @Generable struct Arguments { @Guide(description: "Ce que l'utilisateur cherche, en quelques mots") let query: String } private let store: PocheStore private let index: SemanticIndex init(store: PocheStore, index: SemanticIndex) { self.store = store self.index = index } func call(arguments: Arguments) async throws -> String { guard let query = ToolInput.title(arguments.query) else { return "Erreur : la recherche est vide." } let corpus = await store.corpus() guard !corpus.isEmpty else { return "Aucune donnée enregistrée dans Poche pour l'instant." } let results = await index.rank(query: query, in: corpus, limit: 3) guard !results.isEmpty else { return "Rien trouvé pour « \(query) »." } // Budgeted output: 3 results max, each clipped (~200 tokens total). return results .map { "[\($0.kind)] \($0.title) — \(String($0.body.prefix(160)))" } .joined(separator: "\n") } }