Phase 11: footer status line on·N — SPEC §7.2.

Update skill-reinject status after toggles, clear, skill tracking, and session restore.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-17 12:57:38 +07:00
parent 00ed5c6253
commit 5d902349d1
2 changed files with 35 additions and 8 deletions
+26 -4
View File
@@ -1,7 +1,7 @@
import type { ExtensionAPI, ExtensionCommandContext, Skill } from "@earendil-works/pi-coding-agent";
import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext, Skill } from "@earendil-works/pi-coding-agent";
import { resolveDeliveryMode } from "./auto-compact.js";
import { reinjectNow } from "./reinject.js";
import { readSettings, writeGlobalSettings, type SkillReinjectSettings } from "./settings.js";
import { effectiveEnabled, readSettings, writeGlobalSettings, type SkillReinjectSettings } from "./settings.js";
import type { AutoCompactIntegration, ExtensionState, RuntimeFlags } from "./state.js";
export interface SkillReinjectCommandDeps {
@@ -12,6 +12,20 @@ export interface SkillReinjectCommandDeps {
persistState: () => void;
}
/** Footer status `on·N` / `off·N` (SPEC §7.2). */
export function updateSkillReinjectStatusLine(
ctx: ExtensionContext,
state: ExtensionState,
settings?: SkillReinjectSettings,
): void {
if (!ctx.hasUI) {
return;
}
const resolvedSettings = settings ?? readSettings(ctx);
const label = effectiveEnabled(state.sessionOverride, resolvedSettings) ? "on" : "off";
ctx.ui.setStatus("skill-reinject", `${label}·${state.skills.length}`);
}
const SKILL_REINJECT_COMMAND_NAMES = ["skill-reinject", "sr", "skills-reinject"] as const;
export function registerSkillReinjectCommand(pi: ExtensionAPI, deps: SkillReinjectCommandDeps): void {
@@ -93,6 +107,7 @@ function showSkillReinjectStatus(ctx: ExtensionCommandContext, deps: SkillReinje
}
const settings = readSettings(ctx);
ctx.ui.notify(formatSkillReinjectStatus(deps.state, settings, deps.runtime, deps.state.sessionIntegrationOverride), "info");
updateSkillReinjectStatusLine(ctx, deps.state, settings);
}
async function handleSkillReinjectCommand(
@@ -112,7 +127,7 @@ async function handleSkillReinjectCommand(
return;
}
if (subcommand === "global") {
handleGlobalToggle(trimmed, ctx);
handleGlobalToggle(trimmed, ctx, deps);
return;
}
if (subcommand === "list") {
@@ -160,9 +175,14 @@ function handleSessionToggle(
const settings = readSettings(ctx);
const enabledLine = formatEnabledLine(deps.state.sessionOverride, settings);
ctx.ui.notify(enabledLine, "info");
updateSkillReinjectStatusLine(ctx, deps.state, settings);
}
function handleGlobalToggle(args: string, ctx: ExtensionCommandContext): void {
function handleGlobalToggle(
args: string,
ctx: ExtensionCommandContext,
deps: SkillReinjectCommandDeps,
): void {
const parts = args.trim().split(/\s+/);
const action = parts[1];
if (action !== "on" && action !== "off") {
@@ -176,6 +196,7 @@ function handleGlobalToggle(args: string, ctx: ExtensionCommandContext): void {
return;
}
ctx.ui.notify(`skill-reinject: global ${action}`, "info");
updateSkillReinjectStatusLine(ctx, deps.state, readSettings(ctx));
}
function showTrackedSkillsList(ctx: ExtensionCommandContext, deps: SkillReinjectCommandDeps): void {
@@ -199,6 +220,7 @@ function handleClearTrackedSkills(ctx: ExtensionCommandContext, deps: SkillReinj
return;
}
ctx.ui.notify("skill-reinject: cleared tracked skills", "info");
updateSkillReinjectStatusLine(ctx, deps.state);
}
const INTEGRATION_VALUES: readonly AutoCompactIntegration[] = ["auto", "defer", "immediate", "off"];
+9 -4
View File
@@ -7,7 +7,7 @@ import {
type Skill,
} from "@earendil-works/pi-coding-agent";
import { detectAndCachePiAutoCompact, resolveDeliveryMode } from "./auto-compact.js";
import { registerSkillReinjectCommand } from "./commands.js";
import { registerSkillReinjectCommand, updateSkillReinjectStatusLine } from "./commands.js";
import {
consumeCompactionOnSessionCompact,
createCompactionRuntime,
@@ -53,9 +53,12 @@ export default function skillReinject(pi: ExtensionAPI): void {
persistState,
});
function trackSkillAndPersist(input: TrackSkillInput): void {
function trackSkillAndPersist(input: TrackSkillInput, ctx?: ExtensionContext): void {
trackSkill(state, input);
persistState();
if (ctx) {
updateSkillReinjectStatusLine(ctx, state, readSettings(ctx));
}
}
function trackReadSkillPath(path: string, ctx: ExtensionContext): void {
@@ -70,7 +73,7 @@ export default function skillReinject(pi: ExtensionAPI): void {
filePath: matched.filePath,
baseDir: matched.baseDir,
source: "read",
});
}, ctx);
}
function restoreSessionState(ctx: ExtensionContext): void {
@@ -82,6 +85,7 @@ export default function skillReinject(pi: ExtensionAPI): void {
if (!loaded) {
rescanSkillsFromBranch(state, branch, ctx.cwd, registeredSkills, settings.trackReadPaths);
}
updateSkillReinjectStatusLine(ctx, state, settings);
}
function handleSessionCompact(event: SessionCompactEvent, ctx: ExtensionContext): void {
@@ -169,7 +173,7 @@ export default function skillReinject(pi: ExtensionAPI): void {
filePath: skill.filePath,
baseDir: skill.baseDir,
source: "slash",
});
}, ctx);
}
}
@@ -197,6 +201,7 @@ export default function skillReinject(pi: ExtensionAPI): void {
});
}
persistState();
updateSkillReinjectStatusLine(ctx, state, readSettings(ctx));
});
pi.on("tool_call", async (event, ctx) => {