import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry" import type { BmClient } from "../bm-client.ts" import type { BasicMemoryConfig } from "../config.ts" import { log } from "../logger.ts" export function registerCli( api: OpenClawPluginApi, client: BmClient, cfg: BasicMemoryConfig, ): void { api.registerCli( // biome-ignore lint/suspicious/noExplicitAny: openclaw SDK does not ship types ({ program }: { program: any }) => { const cmd = program .command("basic-memory") .description("Basic Memory knowledge graph commands") cmd .command("search") .argument("", "Search query") .option("--limit ", "Max results", "10") .action(async (query: string, opts: { limit: string }) => { const limit = Number.parseInt(opts.limit, 10) || 10 log.debug(`cli search: query="${query}" limit=${limit}`) const results = await client.search(query, limit) if (results.length === 0) { console.log("No notes found.") return } for (const r of results) { const score = r.score ? ` (${(r.score * 100).toFixed(0)}%)` : "" console.log(`- ${r.title}${score}`) if (r.content) { const preview = r.content.length > 100 ? `${r.content.slice(0, 100)}...` : r.content console.log(` ${preview}`) } } }) cmd .command("read") .argument("", "Note title, permalink, or memory:// URL") .option("--raw", "Return raw markdown including frontmatter", false) .action(async (identifier: string, opts: { raw?: boolean }) => { log.debug(`cli read: identifier="${identifier}"`) const note = await client.readNote(identifier, { includeFrontmatter: opts.raw === true, }) console.log(`# ${note.title}`) console.log(`permalink: ${note.permalink}`) console.log(`file: ${note.file_path}`) console.log("") console.log(note.content) }) cmd .command("edit") .argument("", "Note title, permalink, or memory:// URL") .requiredOption( "--operation ", "Edit operation: append|prepend|find_replace|replace_section", ) .requiredOption("--content ", "Edit content") .option("--find-text ", "Text to find for find_replace") .option("--section ", "Section heading for replace_section") .option( "--expected-replacements ", "Expected replacement count for find_replace", "1", ) .action( async ( identifier: string, opts: { operation: | "append" | "prepend" | "find_replace" | "replace_section" content: string findText?: string section?: string expectedReplacements: string }, ) => { const expectedReplacements = Number.parseInt(opts.expectedReplacements, 10) || 1 log.debug( `cli edit: identifier="${identifier}" op=${opts.operation} expected_replacements=${expectedReplacements}`, ) const result = await client.editNote( identifier, opts.operation, opts.content, { find_text: opts.findText, section: opts.section, expected_replacements: expectedReplacements, }, ) console.log(`Edited: ${result.title}`) console.log(`permalink: ${result.permalink}`) console.log(`file: ${result.file_path}`) console.log(`operation: ${result.operation}`) if (result.checksum) { console.log(`checksum: ${result.checksum}`) } }, ) cmd .command("context") .argument("", "Memory URL to navigate") .option("--depth ", "Relation hops to follow", "1") .action(async (url: string, opts: { depth: string }) => { const depth = Number.parseInt(opts.depth, 10) || 1 log.debug(`cli context: url="${url}" depth=${depth}`) const ctx = await client.buildContext(url, depth) if (!ctx.results || ctx.results.length === 0) { console.log(`No context found for "${url}".`) return } for (const result of ctx.results) { console.log(`## ${result.primary_result.title}`) console.log(result.primary_result.content) console.log("") } }) cmd .command("recent") .option("--timeframe ", "Timeframe (e.g. 24h, 7d)", "24h") .action(async (opts: { timeframe: string }) => { log.debug(`cli recent: timeframe="${opts.timeframe}"`) const results = await client.recentActivity(opts.timeframe) if (results.length === 0) { console.log("No recent activity.") return } for (const r of results) { console.log(`- ${r.title} (${r.permalink})`) } }) cmd .command("status") .description("Show plugin status") .action(() => { console.log(`Project: ${cfg.project}`) console.log(`Project path: ${cfg.projectPath}`) console.log(`BM CLI: ${cfg.bmPath}`) console.log(`Memory dir: ${cfg.memoryDir}`) console.log(`Memory file: ${cfg.memoryFile}`) console.log(`Auto-capture: ${cfg.autoCapture}`) console.log(`Debug: ${cfg.debug}`) }) }, { commands: ["basic-memory"] }, ) }