From 9896e7efa6dbdd7328f30705939583c2688570fa Mon Sep 17 00:00:00 2001 From: GRayHook Date: Wed, 17 Jun 2026 11:19:05 +0700 Subject: [PATCH] =?UTF-8?q?Phase=205:=20add=20skillsPresentInKeptWindow=20?= =?UTF-8?q?=E2=80=94=20detect=20skill=20blocks=20in=20kept=20user=20messag?= =?UTF-8?q?es.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scans kept entries for expanded skill blocks so re-inject skips duplicates per SPEC §6.4. Co-authored-by: Cursor --- src/kept.ts | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) 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; +}