From edcf5352c42346e4c0e8227778ac45ba16af7786 Mon Sep 17 00:00:00 2001 From: GRayHook Date: Wed, 17 Jun 2026 10:04:11 +0700 Subject: [PATCH] =?UTF-8?q?Phase=201:=20add=20loadStateFromBranch=20?= =?UTF-8?q?=E2=80=94=20restore=20last=20state=20entry.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Walk branch backwards for skill-reinject:state custom entries per SPEC §6.3; return structuredClone of valid v1 snapshot or null for full rescan. Co-authored-by: Cursor --- src/state.ts | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/state.ts b/src/state.ts index be97fa0..03597b6 100644 --- a/src/state.ts +++ b/src/state.ts @@ -1,4 +1,4 @@ -import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; +import type { ExtensionAPI, SessionEntry } from "@earendil-works/pi-coding-agent"; /** How a skill was first observed in the session (SPEC §6.2). */ export type SkillSource = "slash" | "skill-block" | "read"; @@ -39,6 +39,37 @@ export function saveState(pi: ExtensionAPI, state: ExtensionState): void { pi.appendEntry(STATE_ENTRY_TYPE, state); } +function isExtensionState(data: unknown): data is ExtensionState { + if (!data || typeof data !== "object") { + return false; + } + const candidate = data as ExtensionState; + return ( + candidate.version === 1 && + (candidate.sessionOverride === null || typeof candidate.sessionOverride === "boolean") && + Array.isArray(candidate.skills) && + (candidate.lastCompactionSource === null || + candidate.lastCompactionSource === "auto" || + candidate.lastCompactionSource === "manual") && + Array.isArray(candidate.pendingReinject) + ); +} + +/** Latest persisted state on the branch, or null if none (SPEC §6.3). */ +export function loadStateFromBranch(branch: SessionEntry[]): ExtensionState | null { + for (let i = branch.length - 1; i >= 0; i--) { + const entry = branch[i]; + if (entry.type !== "custom" || entry.customType !== STATE_ENTRY_TYPE || entry.data === undefined) { + continue; + } + if (!isExtensionState(entry.data)) { + continue; + } + return structuredClone(entry.data); + } + return null; +} + export function createInitialState(): ExtensionState { return { version: 1,