diff --git a/src/commands.ts b/src/commands.ts index cfdd473..e2e34e9 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -1,5 +1,7 @@ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent"; -import type { ExtensionState, RuntimeFlags } from "./state.js"; +import { resolveDeliveryMode } from "./auto-compact.js"; +import { readSettings, type SkillReinjectSettings } from "./settings.js"; +import type { AutoCompactIntegration, ExtensionState, RuntimeFlags } from "./state.js"; export interface SkillReinjectCommandDeps { state: ExtensionState; @@ -7,20 +9,93 @@ export interface SkillReinjectCommandDeps { persistState: () => void; } -export function registerSkillReinjectCommand(pi: ExtensionAPI, _deps: SkillReinjectCommandDeps): void { +export function registerSkillReinjectCommand(pi: ExtensionAPI, deps: SkillReinjectCommandDeps): void { pi.registerCommand("skill-reinject", { description: "Skill re-inject after compaction (usage: /skill-reinject [on|off|list|now|integration ...])", - handler: handleSkillReinjectCommand, + handler: async (args, ctx) => { + await handleSkillReinjectCommand(args, ctx, deps); + }, }); } +function formatEnabledLine(sessionOverride: boolean | null, settings: SkillReinjectSettings): string { + if (sessionOverride === true) { + return "skill-reinject: on (session)"; + } + if (sessionOverride === false) { + return "skill-reinject: off (session override)"; + } + if (settings.enabled) { + return "skill-reinject: on (global)"; + } + return "skill-reinject: off (global)"; +} + +function formatDeliveryLine( + settings: SkillReinjectSettings, + runtime: RuntimeFlags, + sessionIntegrationOverride?: AutoCompactIntegration | null, +): string { + const mode = resolveDeliveryMode(settings, runtime, sessionIntegrationOverride); + if (mode === "immediate") { + return "delivery: immediate"; + } + if (runtime.autoCompactDetected) { + return "delivery: defer (pi-auto-compact detected)"; + } + return "delivery: defer"; +} + +function formatTrackedLine(state: ExtensionState): string { + const count = state.skills.length; + const suffix = count === 1 ? "" : "s"; + const names = state.skills.map((skill) => skill.name).join(", "); + if (count === 0) { + return "tracked: 0 skills"; + } + return `tracked: ${count} skill${suffix} — ${names}`; +} + +function formatLastCompactionLine(state: ExtensionState): string { + if (!state.lastCompactionSource) { + return "last compaction: none"; + } + return `last compaction: ${state.lastCompactionSource}`; +} + +/** Status text for bare `/skill-reinject` (SPEC §7.2). */ +export function formatSkillReinjectStatus( + state: ExtensionState, + settings: SkillReinjectSettings, + runtime: RuntimeFlags, + sessionIntegrationOverride?: AutoCompactIntegration | null, +): string { + return [ + formatEnabledLine(state.sessionOverride, settings), + formatDeliveryLine(settings, runtime, sessionIntegrationOverride), + formatTrackedLine(state), + `pending reinject: ${state.pendingReinject.length}`, + formatLastCompactionLine(state), + ].join("\n"); +} + +function showSkillReinjectStatus(ctx: ExtensionCommandContext, deps: SkillReinjectCommandDeps): void { + if (!ctx.hasUI) { + return; + } + const settings = readSettings(ctx); + ctx.ui.notify(formatSkillReinjectStatus(deps.state, settings, deps.runtime), "info"); +} + async function handleSkillReinjectCommand( args: string, ctx: ExtensionCommandContext, + deps: SkillReinjectCommandDeps, ): Promise { const trimmed = args.trim(); if (!trimmed) { + showSkillReinjectStatus(ctx, deps); return; }