mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
5365f971ef
Signed-off-by: phernandez <paul@basicmachines.co>
145 lines
4.2 KiB
TypeScript
145 lines
4.2 KiB
TypeScript
import { Type } from "@sinclair/typebox"
|
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"
|
|
import type { BmClient } from "../bm-client.ts"
|
|
import { log } from "../logger.ts"
|
|
|
|
export function registerSearchTool(
|
|
api: OpenClawPluginApi,
|
|
client: BmClient,
|
|
): void {
|
|
api.registerTool(
|
|
{
|
|
name: "search_notes",
|
|
label: "Knowledge Search",
|
|
description:
|
|
"Search the Basic Memory knowledge graph for relevant notes, concepts, and connections. " +
|
|
"Returns matching notes with titles, content previews, and relevance scores. " +
|
|
"Optionally filter by frontmatter metadata fields, tags, or status.",
|
|
parameters: Type.Object({
|
|
query: Type.String({ description: "Search query" }),
|
|
limit: Type.Optional(
|
|
Type.Number({ description: "Max results (default: 10)" }),
|
|
),
|
|
project: Type.Optional(
|
|
Type.String({
|
|
description: "Target project name (defaults to current project)",
|
|
}),
|
|
),
|
|
metadata_filters: Type.Optional(
|
|
Type.Object(
|
|
{},
|
|
{
|
|
additionalProperties: true,
|
|
description:
|
|
"Filter by frontmatter fields. Supports equality, $in, $gt/$gte/$lt/$lte, $between, and array-contains operators.",
|
|
},
|
|
),
|
|
),
|
|
tags: Type.Optional(
|
|
Type.Array(Type.String(), {
|
|
description: "Filter by frontmatter tags (all must match)",
|
|
}),
|
|
),
|
|
status: Type.Optional(
|
|
Type.String({
|
|
description: "Filter by frontmatter status field",
|
|
}),
|
|
),
|
|
}),
|
|
async execute(
|
|
_toolCallId: string,
|
|
params: {
|
|
query: string
|
|
limit?: number
|
|
project?: string
|
|
metadata_filters?: Record<string, unknown>
|
|
tags?: string[]
|
|
status?: string
|
|
},
|
|
) {
|
|
const limit = params.limit ?? 10
|
|
log.debug(
|
|
`search_notes: query="${params.query}" limit=${limit} project="${params.project ?? "default"}"`,
|
|
)
|
|
|
|
const metadata =
|
|
params.metadata_filters || params.tags || params.status
|
|
? {
|
|
filters: params.metadata_filters,
|
|
tags: params.tags,
|
|
status: params.status,
|
|
}
|
|
: undefined
|
|
|
|
try {
|
|
const results = await client.search(
|
|
params.query,
|
|
limit,
|
|
params.project,
|
|
metadata,
|
|
)
|
|
|
|
if (results.length === 0) {
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text" as const,
|
|
text: "No matching notes found in the knowledge graph.",
|
|
},
|
|
],
|
|
details: {
|
|
count: 0,
|
|
results: [],
|
|
},
|
|
}
|
|
}
|
|
|
|
const text = results
|
|
.map((r, i) => {
|
|
const score = r.score ? ` (${(r.score * 100).toFixed(0)}%)` : ""
|
|
const content = r.content ?? ""
|
|
const preview =
|
|
content.length > 200 ? `${content.slice(0, 200)}...` : content
|
|
return `${i + 1}. **${r.title}**${score}\n ${preview}`
|
|
})
|
|
.join("\n\n")
|
|
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text" as const,
|
|
text: `Found ${results.length} notes:\n\n${text}`,
|
|
},
|
|
],
|
|
details: {
|
|
count: results.length,
|
|
results: results.map((r) => ({
|
|
title: r.title,
|
|
permalink: r.permalink,
|
|
score: r.score,
|
|
})),
|
|
},
|
|
}
|
|
} catch (err) {
|
|
log.error("search_notes failed", err)
|
|
return {
|
|
content: [
|
|
{
|
|
type: "text" as const,
|
|
text: "Search failed. Is Basic Memory running? Check logs for details.",
|
|
},
|
|
],
|
|
details: {
|
|
count: 0,
|
|
results: [],
|
|
query: params.query,
|
|
error: "search_notes_failed",
|
|
},
|
|
}
|
|
}
|
|
},
|
|
},
|
|
{ name: "search_notes" },
|
|
)
|
|
}
|