Phase 9: track skill blocks on message_end for user messages — SPEC §6.2 #2.
Scan finalized user message text for expanded skill XML blocks and upsert tracked skills using registered metadata when available. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -21,6 +21,27 @@ function normalizePathForCompare(filePath: string): string {
|
|||||||
return normalize(filePath);
|
return normalize(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Text from user message content (string or text blocks). */
|
||||||
|
export function userMessageText(content: unknown): string {
|
||||||
|
if (typeof content === "string") {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
if (!Array.isArray(content)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const parts: string[] = [];
|
||||||
|
for (const part of content) {
|
||||||
|
if (!part || typeof part !== "object") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const block = part as { type?: string; text?: string };
|
||||||
|
if (block.type === "text" && typeof block.text === "string") {
|
||||||
|
parts.push(block.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parts.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns skill name when text is a slash skill command, else null. */
|
/** Returns skill name when text is a slash skill command, else null. */
|
||||||
export function detectSlashSkill(text: string): string | null {
|
export function detectSlashSkill(text: string): string | null {
|
||||||
const match = SLASH_SKILL_RE.exec(text);
|
const match = SLASH_SKILL_RE.exec(text);
|
||||||
|
|||||||
+20
-1
@@ -1,5 +1,6 @@
|
|||||||
|
import { dirname } from "node:path";
|
||||||
import type { ExtensionAPI, Skill } from "@earendil-works/pi-coding-agent";
|
import type { ExtensionAPI, Skill } from "@earendil-works/pi-coding-agent";
|
||||||
import { detectSlashSkill } from "./detect.js";
|
import { detectSlashSkill, parseSkillBlocksFromText, userMessageText } from "./detect.js";
|
||||||
import { findRegisteredSkillByName, resolveRegisteredSkills } from "./skills-registry.js";
|
import { findRegisteredSkillByName, resolveRegisteredSkills } from "./skills-registry.js";
|
||||||
import { createInitialState, trackSkill } from "./state.js";
|
import { createInitialState, trackSkill } from "./state.js";
|
||||||
|
|
||||||
@@ -32,4 +33,22 @@ export default function skillReinject(pi: ExtensionAPI): void {
|
|||||||
|
|
||||||
return { action: "continue" };
|
return { action: "continue" };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pi.on("message_end", async (event, ctx) => {
|
||||||
|
if (event.message.role !== "user") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = userMessageText(event.message.content);
|
||||||
|
const skills = resolveRegisteredSkills(ctx.cwd, registeredSkills);
|
||||||
|
for (const block of parseSkillBlocksFromText(text)) {
|
||||||
|
const registered = findRegisteredSkillByName(skills, block.name);
|
||||||
|
trackSkill(state, {
|
||||||
|
name: block.name,
|
||||||
|
filePath: registered?.filePath ?? block.location,
|
||||||
|
baseDir: registered?.baseDir ?? dirname(block.location),
|
||||||
|
source: "skill-block",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user