Phase 9: persist state after tracked skill changes — SPEC §6.1.
Call saveState via appendEntry after slash, skill-block, and read-path tracking so session state survives resume. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+39
-25
@@ -3,32 +3,41 @@ import { isToolCallEventType, type ExtensionAPI, type ExtensionContext, type Ski
|
||||
import { detectSlashSkill, matchReadPathToSkillWhenEnabled, parseSkillBlocksFromText, userMessageText } from "./detect.js";
|
||||
import { readSettings } from "./settings.js";
|
||||
import { findRegisteredSkillByName, resolveRegisteredSkills } from "./skills-registry.js";
|
||||
import { createInitialState, trackSkill, type ExtensionState } from "./state.js";
|
||||
|
||||
function trackReadSkillPath(
|
||||
path: string,
|
||||
ctx: ExtensionContext,
|
||||
state: ExtensionState,
|
||||
registeredSkills: Skill[],
|
||||
): void {
|
||||
const settings = readSettings(ctx);
|
||||
const skills = resolveRegisteredSkills(ctx.cwd, registeredSkills);
|
||||
const matched = matchReadPathToSkillWhenEnabled(path, skills, settings.trackReadPaths);
|
||||
if (!matched) {
|
||||
return;
|
||||
}
|
||||
trackSkill(state, {
|
||||
name: matched.name,
|
||||
filePath: matched.filePath,
|
||||
baseDir: matched.baseDir,
|
||||
source: "read",
|
||||
});
|
||||
}
|
||||
import {
|
||||
createInitialState,
|
||||
saveState,
|
||||
trackSkill,
|
||||
type TrackSkillInput,
|
||||
} from "./state.js";
|
||||
|
||||
export default function skillReinject(pi: ExtensionAPI): void {
|
||||
const state = createInitialState();
|
||||
let registeredSkills: Skill[] = [];
|
||||
|
||||
function persistState(): void {
|
||||
saveState(pi, state);
|
||||
}
|
||||
|
||||
function trackSkillAndPersist(input: TrackSkillInput): void {
|
||||
trackSkill(state, input);
|
||||
persistState();
|
||||
}
|
||||
|
||||
function trackReadSkillPath(path: string, ctx: ExtensionContext): void {
|
||||
const settings = readSettings(ctx);
|
||||
const skills = resolveRegisteredSkills(ctx.cwd, registeredSkills);
|
||||
const matched = matchReadPathToSkillWhenEnabled(path, skills, settings.trackReadPaths);
|
||||
if (!matched) {
|
||||
return;
|
||||
}
|
||||
trackSkillAndPersist({
|
||||
name: matched.name,
|
||||
filePath: matched.filePath,
|
||||
baseDir: matched.baseDir,
|
||||
source: "read",
|
||||
});
|
||||
}
|
||||
|
||||
pi.on("before_agent_start", async (event) => {
|
||||
registeredSkills = event.systemPromptOptions.skills ?? registeredSkills;
|
||||
});
|
||||
@@ -43,7 +52,7 @@ export default function skillReinject(pi: ExtensionAPI): void {
|
||||
const skills = resolveRegisteredSkills(ctx.cwd, registeredSkills);
|
||||
const skill = findRegisteredSkillByName(skills, skillName);
|
||||
if (skill) {
|
||||
trackSkill(state, {
|
||||
trackSkillAndPersist({
|
||||
name: skill.name,
|
||||
filePath: skill.filePath,
|
||||
baseDir: skill.baseDir,
|
||||
@@ -60,9 +69,13 @@ export default function skillReinject(pi: ExtensionAPI): void {
|
||||
return;
|
||||
}
|
||||
|
||||
const text = userMessageText(event.message.content);
|
||||
const blocks = parseSkillBlocksFromText(userMessageText(event.message.content));
|
||||
if (blocks.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const skills = resolveRegisteredSkills(ctx.cwd, registeredSkills);
|
||||
for (const block of parseSkillBlocksFromText(text)) {
|
||||
for (const block of blocks) {
|
||||
const registered = findRegisteredSkillByName(skills, block.name);
|
||||
trackSkill(state, {
|
||||
name: block.name,
|
||||
@@ -71,12 +84,13 @@ export default function skillReinject(pi: ExtensionAPI): void {
|
||||
source: "skill-block",
|
||||
});
|
||||
}
|
||||
persistState();
|
||||
});
|
||||
|
||||
pi.on("tool_call", async (event, ctx) => {
|
||||
if (!isToolCallEventType("read", event)) {
|
||||
return;
|
||||
}
|
||||
trackReadSkillPath(event.input.path, ctx, state, registeredSkills);
|
||||
trackReadSkillPath(event.input.path, ctx);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user