diff --git a/src/reinject.ts b/src/reinject.ts index febd92d..67761eb 100644 --- a/src/reinject.ts +++ b/src/reinject.ts @@ -1,8 +1,10 @@ import type { + BeforeAgentStartEventResult, ExtensionContext, SessionCompactEvent, Skill, } from "@earendil-works/pi-coding-agent"; +import { expandSkill } from "./expand.js"; import { filterSkillsNeedingReinject, getKeptEntries, @@ -11,6 +13,8 @@ import { import type { SkillReinjectSettings } from "./settings.js"; import type { ExtensionState } from "./state.js"; +export const DEFERRED_REINJECT_CUSTOM_TYPE = "skill-reinject:inject"; + /** Names still registered in resourceLoader (SPEC §5.2). */ export function registeredSkillNames(skills: readonly Pick[]): ReadonlySet { return new Set(skills.map((skill) => skill.name)); @@ -54,3 +58,60 @@ export function enqueueDeferredReinjectFromCompact( registeredSkills, ); } + +/** Combined skill-block user text for pending names in queue order (SPEC §5.3). */ +export function buildDeferredReinjectContent( + pendingNames: readonly string[], + state: ExtensionState, + settings: SkillReinjectSettings, + registeredSkills: readonly Pick[], +): string { + const registeredByName = new Map(registeredSkills.map((skill) => [skill.name, skill])); + const blocks: string[] = []; + for (const name of pendingNames) { + const tracked = state.skills.find((skill) => skill.name === name); + const registered = registeredByName.get(name); + if (!tracked || !registered) { + continue; + } + blocks.push( + expandSkill( + { + name: tracked.name, + filePath: registered.filePath, + baseDir: registered.baseDir, + }, + settings.suffix, + ), + ); + } + return blocks.join("\n\n"); +} + +/** + * Defer path on before_agent_start: inject one combined message, then clear queue (SPEC §6.5.1). + * Returns undefined when pendingReinject is empty. + */ +export function tryConsumeDeferredReinject( + state: ExtensionState, + settings: SkillReinjectSettings, + registeredSkills: readonly Pick[], +): BeforeAgentStartEventResult | undefined { + if (state.pendingReinject.length === 0) { + return undefined; + } + const pendingNames = [...state.pendingReinject]; + const content = buildDeferredReinjectContent(pendingNames, state, settings, registeredSkills); + if (!content) { + state.pendingReinject = []; + return undefined; + } + state.pendingReinject = []; + return { + message: { + customType: DEFERRED_REINJECT_CUSTOM_TYPE, + content, + display: true, + }, + }; +}