diff --git a/src/kept.ts b/src/kept.ts index ad0a446..9461103 100644 --- a/src/kept.ts +++ b/src/kept.ts @@ -1,4 +1,5 @@ import type { SessionEntry } from "@earendil-works/pi-coding-agent"; +import { parseSkillBlocksFromText } from "./detect.js"; /** Branch slice from firstKeptEntryId through tail (SPEC §6.4). */ export function getKeptEntries(branch: SessionEntry[], firstKeptEntryId: string): SessionEntry[] { @@ -8,3 +9,48 @@ export function getKeptEntries(branch: SessionEntry[], firstKeptEntryId: string) } return branch.slice(startIndex); } + +function extractUserMessageText(content: unknown): string { + if (typeof content === "string") { + return content; + } + if (!Array.isArray(content)) { + return ""; + } + const parts: string[] = []; + for (const part of content) { + if (!part || typeof part !== "object") { + continue; + } + const block = part as { type?: string; text?: string }; + if (block.type === "text" && typeof block.text === "string") { + parts.push(block.text); + } + } + return parts.join("\n"); +} + +/** Skill names already present as blocks in kept user messages (SPEC §6.4). */ +export function skillsPresentInKeptWindow( + keptEntries: SessionEntry[], + skillNames: readonly string[], +): Set { + const present = new Set(); + const namesToCheck = new Set(skillNames); + if (namesToCheck.size === 0) { + return present; + } + + for (const entry of keptEntries) { + if (entry.type !== "message" || entry.message.role !== "user") { + continue; + } + const text = extractUserMessageText(entry.message.content); + for (const block of parseSkillBlocksFromText(text)) { + if (namesToCheck.has(block.name)) { + present.add(block.name); + } + } + } + return present; +}