akm-cli
Version:
akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cur
71 lines (70 loc) • 3.36 kB
JavaScript
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
import { displayRef } from "../../core/asset/resolve-ref.js";
import { presentationFor } from "../../core/type-presentation.js";
import { getDerivedForParent, getItemRefById } from "../../storage/repositories/index-entries-repository.js";
import { getRenderer } from "../walk/file-context.js";
import { attachSearchHitAttribution } from "./search-attribution.js";
/**
* Let the hit's type renderer enrich it, then — for a memory, when a DB is
* open — surface its derived child ({@link surfaceDerivedMemory}).
*/
export async function enrichSearchHit(hit, ctx) {
const rendererName = presentationFor(ctx.type).renderer;
if (rendererName) {
const renderer = await getRenderer(rendererName);
renderer?.enrichSearchHit?.(hit, ctx.stashDir);
}
if (ctx.type === "memory" && ctx.db)
surfaceDerivedMemory(hit, ctx.db, ctx.bundleId);
}
/**
* Phase 5A / Advantage D5 — when a parent memory has a `.derived` child
* indexed (the LLM-distilled lesson surface), rewrite the parent hit to
* surface the derived child's description / tags AND set `expandTo` to the
* derived child's ref so callers can fetch it via `akm show <ref>`.
*
* The parent ref is preserved on the hit — only the surface text is
* swapped, so links and provenance still point at the canonical parent.
*/
function surfaceDerivedMemory(hit, db, bundleId) {
// Never recurse: a `.derived` hit is itself the child surface; leaving
// it untouched also avoids `<parent>.derived.derived` chains.
if (hit.name.toLowerCase().endsWith(".derived"))
return;
// Parent ref shape: the 0.9.0 `memories/<name>` conceptId. Re-build from the
// entry's name so we don't depend on whatever wiki/registry prefix `hit.ref`
// carries. INTERNAL lookup key into `getDerivedForParent`: the `derived_from`
// column stores this same conceptId grammar.
const derived = getDerivedForParent(db, `memories/${hit.name}`, bundleId);
if (!derived)
return;
// Swap description / tags from the derived child (SourceSearchHit carries no
// `searchHints`).
const surfaceFields = [];
let surfaceDescription;
if (typeof derived.entry.description === "string" && derived.entry.description.length > 0) {
hit.description = derived.entry.description;
surfaceDescription = derived.entry.description;
surfaceFields.push("description");
}
if (Array.isArray(derived.entry.tags) && derived.entry.tags.length > 0) {
hit.tags = derived.entry.tags;
surfaceFields.push("tags");
}
// `expandTo` is a user-facing `akm show <ref>` target, so emit the 0.9.0
// short conceptId grammar (`memories/<name>`).
hit.expandTo = displayRef({ type: "memory", name: derived.entry.name });
const childRef = getItemRefById(db, derived.id);
if (childRef && surfaceFields.length > 0) {
attachSearchHitAttribution(hit, {
memoryInference: {
exposure: "surface",
childRef,
surfaceFields,
...(surfaceDescription ? { surfaceDescription } : {}),
},
});
}
}