From 03dcdb22de6f0277975864159249b491676fd3d6 Mon Sep 17 00:00:00 2001 From: GRayHook Date: Wed, 17 Jun 2026 12:56:19 +0700 Subject: [PATCH] =?UTF-8?q?Phase=2011:=20integration=20session=20override?= =?UTF-8?q?=20=E2=80=94=20SPEC=20=C2=A77.1,=20=C2=A716.4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Persist autoCompactIntegration override in state and wire resolveDeliveryMode plus status delivery line. Co-authored-by: Cursor --- src/commands.ts | 36 +++++++++++++++++++++++++++++++++++- src/index.ts | 2 +- src/state.ts | 10 ++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/commands.ts b/src/commands.ts index 5b4085b..9aa779e 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -85,7 +85,7 @@ function showSkillReinjectStatus(ctx: ExtensionCommandContext, deps: SkillReinje return; } const settings = readSettings(ctx); - ctx.ui.notify(formatSkillReinjectStatus(deps.state, settings, deps.runtime), "info"); + ctx.ui.notify(formatSkillReinjectStatus(deps.state, settings, deps.runtime, deps.state.sessionIntegrationOverride), "info"); } async function handleSkillReinjectCommand( @@ -116,6 +116,10 @@ async function handleSkillReinjectCommand( handleClearTrackedSkills(ctx, deps); return; } + if (subcommand === "integration") { + handleIntegrationOverride(trimmed, ctx, deps); + return; + } if (ctx.hasUI) { ctx.ui.notify(`skill-reinject: unknown subcommand "${subcommand}"`, "warning"); @@ -185,3 +189,33 @@ function handleClearTrackedSkills(ctx: ExtensionCommandContext, deps: SkillReinj } ctx.ui.notify("skill-reinject: cleared tracked skills", "info"); } + +const INTEGRATION_VALUES: readonly AutoCompactIntegration[] = ["auto", "defer", "immediate", "off"]; + +function isAutoCompactIntegration(value: string): value is AutoCompactIntegration { + return (INTEGRATION_VALUES as readonly string[]).includes(value); +} + +function handleIntegrationOverride( + args: string, + ctx: ExtensionCommandContext, + deps: SkillReinjectCommandDeps, +): void { + const mode = args.trim().split(/\s+/)[1]; + if (!mode || !isAutoCompactIntegration(mode)) { + if (ctx.hasUI) { + ctx.ui.notify( + "skill-reinject: usage: /skill-reinject integration auto|defer|immediate|off", + "warning", + ); + } + return; + } + deps.state.sessionIntegrationOverride = mode; + deps.persistState(); + if (!ctx.hasUI) { + return; + } + const settings = readSettings(ctx); + ctx.ui.notify(formatDeliveryLine(settings, deps.runtime, mode), "info"); +} diff --git a/src/index.ts b/src/index.ts index 9cecc7c..d4b6bff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -92,7 +92,7 @@ export default function skillReinject(pi: ExtensionAPI): void { } const skills = resolveRegisteredSkills(ctx.cwd, registeredSkills); - const deliveryMode = resolveDeliveryMode(settings, runtime); + const deliveryMode = resolveDeliveryMode(settings, runtime, state.sessionIntegrationOverride); if (deliveryMode === "defer") { enqueueDeferredReinjectFromCompact(state, settings, ctx, event, skills); persistState(); diff --git a/src/state.ts b/src/state.ts index fe167ee..8719bcd 100644 --- a/src/state.ts +++ b/src/state.ts @@ -21,6 +21,8 @@ export interface TrackedSkill { export interface ExtensionState { version: 1; sessionOverride: boolean | null; + /** Session override for autoCompactIntegration (SPEC §7.1, §16.4). */ + sessionIntegrationOverride: AutoCompactIntegration | null; skills: TrackedSkill[]; lastCompactionSource: CompactionSource | null; /** Skill names awaiting re-inject on the next before_agent_start (SPEC §6.5). */ @@ -49,6 +51,12 @@ function isExtensionState(data: unknown): data is ExtensionState { return ( candidate.version === 1 && (candidate.sessionOverride === null || typeof candidate.sessionOverride === "boolean") && + (candidate.sessionIntegrationOverride === null || + candidate.sessionIntegrationOverride === "auto" || + candidate.sessionIntegrationOverride === "defer" || + candidate.sessionIntegrationOverride === "immediate" || + candidate.sessionIntegrationOverride === "off" || + candidate.sessionIntegrationOverride === undefined) && Array.isArray(candidate.skills) && (candidate.lastCompactionSource === null || candidate.lastCompactionSource === "auto" || @@ -76,6 +84,7 @@ export function createInitialState(): ExtensionState { return { version: 1, sessionOverride: null, + sessionIntegrationOverride: null, skills: [], lastCompactionSource: null, pendingReinject: [], @@ -85,6 +94,7 @@ export function createInitialState(): ExtensionState { /** Copy persisted fields into live session state (SPEC §6.3). */ export function applyExtensionState(target: ExtensionState, loaded: ExtensionState): void { target.sessionOverride = loaded.sessionOverride; + target.sessionIntegrationOverride = loaded.sessionIntegrationOverride ?? null; target.skills = loaded.skills; target.lastCompactionSource = loaded.lastCompactionSource; target.pendingReinject = loaded.pendingReinject;