Files
heygen-com--hyperframes/skills/media-use/scripts/lib/candidates.mjs
T
wehub-resource-sync 85453da49f
regression / regression-shards (style-16-prod style-9-prod style-17-prod iframe-render-compat variables-prod mp4-h265-sdr, shard-4) (push) Has been cancelled
regression / regression-shards (style-4-prod style-11-prod style-2-prod animejs-adapter typegpu-adapter parallel-capture-regression, shard-5) (push) Has been cancelled
regression / regression-shards (style-7-prod style-8-prod style-10-prod css-spinner-render-compat webm-transparency mp4-h264-sdr webm-vp9, shard-3) (push) Has been cancelled
regression / regression-shards (sub-composition-video style-18-prod raf-ball-render-compat font-variant-numeric sub-comp-t0 sub-comp-id-selector, shard-7) (push) Has been cancelled
Windows render verification / Detect changes (push) Has been cancelled
Windows render verification / Preflight (lint + format) (push) Has been cancelled
Windows render verification / Render on windows-latest (push) Has been cancelled
Windows render verification / Tests on windows-latest (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Fallow audit (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Producer: integration tests (push) Has been cancelled
CI / Producer: unit tests (push) Has been cancelled
CI / File size check (push) Has been cancelled
CI / Test: skills (push) Has been cancelled
CI / Skills: manifest in sync (push) Has been cancelled
CI / CLI: npx shim (macos-latest) (push) Has been cancelled
CI / CLI: npx shim (ubuntu-latest) (push) Has been cancelled
CI / CLI: npx shim (windows-latest) (push) Has been cancelled
CI / SDK: unit + contract + smoke (push) Has been cancelled
CI / Test: runtime contract (push) Has been cancelled
CI / Studio: load smoke (push) Has been cancelled
CI / Smoke: global install (push) Has been cancelled
CI / CLI smoke (required) (push) Has been cancelled
CI / Semantic PR title (push) Has been cancelled
Player perf / Detect changes (push) Has been cancelled
Player perf / Preflight (lint + format) (push) Has been cancelled
Player perf / player-perf (push) Has been cancelled
Player perf / Perf: drift (push) Has been cancelled
Player perf / Perf: fps (push) Has been cancelled
Player perf / Perf: parity (push) Has been cancelled
Player perf / Perf: scrub (push) Has been cancelled
Player perf / Perf: load (push) Has been cancelled
preview-regression / Detect changes (push) Has been cancelled
preview-regression / Preflight (lint + format) (push) Has been cancelled
preview-regression / Preview parity (push) Has been cancelled
preview-regression / preview-regression (push) Has been cancelled
regression / regression (push) Has been cancelled
regression / Detect changes (push) Has been cancelled
regression / Preflight (lint + format) (push) Has been cancelled
regression / regression-shards (hdr-regression style-5-prod style-3-prod mov-prores, shard-1) (push) Has been cancelled
regression / regression-shards (overlay-montage-prod style-12-prod chat missing-host-comp-id png-sequence portrait-edge-bleed, shard-6) (push) Has been cancelled
regression / regression-shards (style-13-prod style-6-prod vignelli-stacking gsap-letters-render-compat audio-mux-parity, shard-8) (push) Has been cancelled
regression / regression-shards (style-15-prod hdr-hlg-regression style-1-prod many-cuts vfr-screen-recording render-symlinked-assets, shard-2) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docs / Validate docs (push) Has been cancelled
Sync skills to ClawHub / Publish changed skills (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:58:35 +08:00

92 lines
4.0 KiB
JavaScript

// Reuse candidates: a side-effect-free view of assets already available to this
// project (its own manifest) and across every project (the global ~/.media
// cache), so the calling agent can judge semantic fit itself. No download, no
// provider, no mutation. The ranker only *surfaces* — it orders by lexical
// overlap but never filters a candidate out on zero overlap (that would
// pre-empt the agent's judgment); the agent does the semantic call.
import { readManifest } from "./manifest.mjs";
import { readGlobalManifest } from "./cache.mjs";
import { tokenOverlap, typesMatch } from "./match.mjs";
export const CANDIDATE_CAP = 8;
function shape(record, scope, intent) {
const description = record.description || record.provenance?.prompt || "";
const prompt = record.provenance?.prompt || null;
return {
id: record.id,
type: record.type,
scope,
description,
prompt,
provider: record.provenance?.provider || null,
duration: record.duration ?? null,
width: record.width ?? null,
height: record.height ?? null,
// Only global records carry a content sha — it is the stable reuse handle
// for `resolve --reuse <sha>`. Project assets are reused by referencing
// their path directly, so they need no handle.
sha: scope === "global" ? record.sha || null : null,
path: scope === "project" ? record.path : null,
score: intent ? tokenOverlap(intent, `${description} ${prompt || ""}`) : 0,
};
}
// Rank one scope: type-matched (icon<->image aware), newest-first within equal
// overlap, ordered by overlap desc. Returns the full ranked list (uncapped).
function rankScope(records, scope, type, intent) {
return records
.filter((r) => typesMatch(r.type, type))
.reverse() // manifest is append-order (oldest first); newest-first at equal score
.map((r) => shape(r, scope, intent))
.sort((a, b) => b.score - a.score); // Array.sort is stable → recency preserved
}
// List reuse candidates for `type`, capped per scope. Returns:
// candidates: capped project candidates followed by capped global candidates
// truncated: true if either scope had more than `cap`
// total: { project, global } counts before the cap (machine-readable)
// similar: count of candidates with lexical overlap > 0 (drives the nudge)
export function listCandidates({ projectDir, type, intent = "", cap = CANDIDATE_CAP }) {
const project = rankScope(readManifest(projectDir), "project", type, intent);
const global = rankScope(readGlobalManifest(), "global", type, intent);
const candidates = [...project.slice(0, cap), ...global.slice(0, cap)];
return {
candidates,
truncated: project.length > cap || global.length > cap,
total: { project: project.length, global: global.length },
similar: [...project, ...global].filter((c) => c.score > 0).length,
};
}
function meta(c) {
const parts = [];
if (c.duration != null) parts.push(`${c.duration}s`);
if (c.width && c.height) parts.push(`${c.width}x${c.height}`);
if (c.provider) parts.push(c.provider);
return parts.join(", ");
}
// Human-readable listing. The agent can read this directly; --json is for
// programmatic use. Reuse handle differs by scope: path for project, sha for
// global.
export function formatCandidates(candidates, { truncated, total } = {}) {
if (candidates.length === 0) return "no reuse candidates found (project or global cache)";
const lines = [`${candidates.length} reuse candidate${candidates.length === 1 ? "" : "s"}:`, ""];
for (const c of candidates) {
const handle =
c.scope === "global" ? `--reuse ${String(c.sha).slice(0, 16)}` : c.path || `manifest:${c.id}`;
const m = meta(c);
lines.push(` [${c.scope}] ${c.description}${m ? ` (${m})` : ""}`);
lines.push(` ${handle}`);
}
if (truncated && total) {
lines.push("");
lines.push(
` (showing top ${CANDIDATE_CAP} per scope; ${total.project} project / ${total.global} global total — refine --intent to narrow)`,
);
}
return lines.join("\n");
}