mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 17:51:22 +00:00
Merged via squash.
Prepared head SHA: 5dfe246ef9
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
223 lines
8.5 KiB
TypeScript
223 lines
8.5 KiB
TypeScript
import type { Command } from "commander";
|
|
import {
|
|
formatDocsLink,
|
|
formatHelpExamples,
|
|
theme,
|
|
} from "openclaw/plugin-sdk/memory-core-host-runtime-cli";
|
|
import type {
|
|
MemoryCommandOptions,
|
|
MemoryPromoteCommandOptions,
|
|
MemoryPromoteExplainOptions,
|
|
MemoryRemBackfillOptions,
|
|
MemoryRemHarnessOptions,
|
|
MemorySearchCommandOptions,
|
|
} from "./cli.types.js";
|
|
import {
|
|
DEFAULT_PROMOTION_MIN_RECALL_COUNT,
|
|
DEFAULT_PROMOTION_MIN_SCORE,
|
|
DEFAULT_PROMOTION_MIN_UNIQUE_QUERIES,
|
|
} from "./short-term-promotion.js";
|
|
|
|
type MemoryCliRuntime = typeof import("./cli.runtime.js");
|
|
|
|
let memoryCliRuntimePromise: Promise<MemoryCliRuntime> | null = null;
|
|
|
|
async function loadMemoryCliRuntime(): Promise<MemoryCliRuntime> {
|
|
memoryCliRuntimePromise ??= import("./cli.runtime.js");
|
|
return await memoryCliRuntimePromise;
|
|
}
|
|
|
|
export async function runMemoryStatus(opts: MemoryCommandOptions) {
|
|
const runtime = await loadMemoryCliRuntime();
|
|
await runtime.runMemoryStatus(opts);
|
|
}
|
|
|
|
async function runMemoryIndex(opts: MemoryCommandOptions) {
|
|
const runtime = await loadMemoryCliRuntime();
|
|
await runtime.runMemoryIndex(opts);
|
|
}
|
|
|
|
async function runMemorySearch(queryArg: string | undefined, opts: MemorySearchCommandOptions) {
|
|
const runtime = await loadMemoryCliRuntime();
|
|
await runtime.runMemorySearch(queryArg, opts);
|
|
}
|
|
|
|
async function runMemoryPromote(opts: MemoryPromoteCommandOptions) {
|
|
const runtime = await loadMemoryCliRuntime();
|
|
await runtime.runMemoryPromote(opts);
|
|
}
|
|
|
|
async function runMemoryPromoteExplain(
|
|
selectorArg: string | undefined,
|
|
opts: MemoryPromoteExplainOptions,
|
|
) {
|
|
const runtime = await loadMemoryCliRuntime();
|
|
await runtime.runMemoryPromoteExplain(selectorArg, opts);
|
|
}
|
|
|
|
async function runMemoryRemHarness(opts: MemoryRemHarnessOptions) {
|
|
const runtime = await loadMemoryCliRuntime();
|
|
await runtime.runMemoryRemHarness(opts);
|
|
}
|
|
|
|
async function runMemoryRemBackfill(opts: MemoryRemBackfillOptions) {
|
|
const runtime = await loadMemoryCliRuntime();
|
|
await runtime.runMemoryRemBackfill(opts);
|
|
}
|
|
|
|
export function registerMemoryCli(program: Command) {
|
|
const memory = program
|
|
.command("memory")
|
|
.description("Search, inspect, and reindex memory files")
|
|
.addHelpText(
|
|
"after",
|
|
() =>
|
|
`\n${theme.heading("Examples:")}\n${formatHelpExamples([
|
|
["openclaw memory status", "Show index and provider status."],
|
|
[
|
|
"openclaw memory status --fix",
|
|
"Repair stale recall locks and normalize promotion metadata.",
|
|
],
|
|
["openclaw memory status --deep", "Probe embedding provider readiness."],
|
|
["openclaw memory index --force", "Force a full reindex."],
|
|
['openclaw memory search "meeting notes"', "Quick search using positional query."],
|
|
[
|
|
'openclaw memory search --query "deployment" --max-results 20',
|
|
"Limit results for focused troubleshooting.",
|
|
],
|
|
[
|
|
`openclaw memory promote --limit 10 --min-score ${DEFAULT_PROMOTION_MIN_SCORE}`,
|
|
"Review weighted short-term candidates for long-term memory.",
|
|
],
|
|
[
|
|
"openclaw memory promote --apply",
|
|
"Append top-ranked short-term candidates into MEMORY.md.",
|
|
],
|
|
[
|
|
'openclaw memory promote-explain "router vlan"',
|
|
"Explain why a specific candidate would or would not promote.",
|
|
],
|
|
[
|
|
"openclaw memory rem-harness --json",
|
|
"Preview REM reflections, candidate truths, and deep promotion output.",
|
|
],
|
|
[
|
|
"openclaw memory rem-backfill --path ./memory",
|
|
"Write grounded historical REM entries into DREAMS.md for UI review.",
|
|
],
|
|
[
|
|
"openclaw memory rem-backfill --path ./memory --stage-short-term",
|
|
"Also seed durable grounded candidates into the live short-term promotion store.",
|
|
],
|
|
["openclaw memory status --json", "Output machine-readable JSON (good for scripts)."],
|
|
])}\n\n${theme.muted("Docs:")} ${formatDocsLink("/cli/memory", "docs.openclaw.ai/cli/memory")}\n`,
|
|
);
|
|
|
|
memory
|
|
.command("status")
|
|
.description("Show memory search index status")
|
|
.option("--agent <id>", "Agent id (default: default agent)")
|
|
.option("--json", "Print JSON")
|
|
.option("--deep", "Probe embedding provider availability")
|
|
.option("--index", "Reindex if dirty (implies --deep)")
|
|
.option("--fix", "Repair stale recall locks and normalize promotion metadata")
|
|
.option("--verbose", "Verbose logging", false)
|
|
.action(async (opts: MemoryCommandOptions & { force?: boolean }) => {
|
|
await runMemoryStatus(opts);
|
|
});
|
|
|
|
memory
|
|
.command("index")
|
|
.description("Reindex memory files")
|
|
.option("--agent <id>", "Agent id (default: default agent)")
|
|
.option("--force", "Force full reindex", false)
|
|
.option("--verbose", "Verbose logging", false)
|
|
.action(async (opts: MemoryCommandOptions) => {
|
|
await runMemoryIndex(opts);
|
|
});
|
|
|
|
memory
|
|
.command("search")
|
|
.description("Search memory files")
|
|
.argument("[query]", "Search query")
|
|
.option("--query <text>", "Search query (alternative to positional argument)")
|
|
.option("--agent <id>", "Agent id (default: default agent)")
|
|
.option("--max-results <n>", "Max results", (value: string) => Number(value))
|
|
.option("--min-score <n>", "Minimum score", (value: string) => Number(value))
|
|
.option("--json", "Print JSON")
|
|
.action(async (queryArg: string | undefined, opts: MemorySearchCommandOptions) => {
|
|
await runMemorySearch(queryArg, opts);
|
|
});
|
|
|
|
memory
|
|
.command("promote")
|
|
.description("Rank short-term recalls and optionally append top entries to MEMORY.md")
|
|
.option("--agent <id>", "Agent id (default: default agent)")
|
|
.option("--limit <n>", "Max candidates", (value: string) => Number(value))
|
|
.option(
|
|
"--min-score <n>",
|
|
`Minimum weighted score (default: ${DEFAULT_PROMOTION_MIN_SCORE})`,
|
|
(value: string) => Number(value),
|
|
)
|
|
.option(
|
|
"--min-recall-count <n>",
|
|
`Minimum recall count (default: ${DEFAULT_PROMOTION_MIN_RECALL_COUNT})`,
|
|
(value: string) => Number(value),
|
|
)
|
|
.option(
|
|
"--min-unique-queries <n>",
|
|
`Minimum distinct query count (default: ${DEFAULT_PROMOTION_MIN_UNIQUE_QUERIES})`,
|
|
(value: string) => Number(value),
|
|
)
|
|
.option("--apply", "Append selected candidates to MEMORY.md", false)
|
|
.option("--include-promoted", "Include already promoted candidates", false)
|
|
.option("--json", "Print JSON")
|
|
.action(async (opts: MemoryPromoteCommandOptions) => {
|
|
await runMemoryPromote(opts);
|
|
});
|
|
|
|
memory
|
|
.command("promote-explain")
|
|
.description("Explain a specific promotion candidate and its score breakdown")
|
|
.argument("<selector>", "Candidate key, path fragment, or snippet fragment")
|
|
.option("--agent <id>", "Agent id (default: default agent)")
|
|
.option("--include-promoted", "Include already promoted candidates", false)
|
|
.option("--json", "Print JSON")
|
|
.action(async (selectorArg: string | undefined, opts: MemoryPromoteExplainOptions) => {
|
|
await runMemoryPromoteExplain(selectorArg, opts);
|
|
});
|
|
|
|
memory
|
|
.command("rem-harness")
|
|
.description("Preview REM reflections, candidate truths, and deep promotions without writing")
|
|
.option("--agent <id>", "Agent id (default: default agent)")
|
|
.option("--path <file-or-dir>", "Seed the harness from historical daily memory file(s)")
|
|
.option("--grounded", "Also render a grounded day-level REM preview")
|
|
.option("--include-promoted", "Include already promoted deep candidates", false)
|
|
.option("--json", "Print JSON")
|
|
.action(async (opts: MemoryRemHarnessOptions) => {
|
|
await runMemoryRemHarness(opts);
|
|
});
|
|
|
|
memory
|
|
.command("rem-backfill")
|
|
.description("Write grounded historical REM summaries into DREAMS.md for UI review")
|
|
.option("--agent <id>", "Agent id (default: default agent)")
|
|
.option("--path <file-or-dir>", "Historical daily memory file(s) or directory")
|
|
.option("--rollback", "Remove previously written grounded REM backfill entries", false)
|
|
.option(
|
|
"--stage-short-term",
|
|
"Also seed grounded durable candidates into the short-term promotion store",
|
|
false,
|
|
)
|
|
.option(
|
|
"--rollback-short-term",
|
|
"Remove previously seeded grounded short-term candidates",
|
|
false,
|
|
)
|
|
.option("--json", "Print JSON")
|
|
.action(async (opts: MemoryRemBackfillOptions) => {
|
|
await runMemoryRemBackfill(opts);
|
|
});
|
|
}
|