From 86c6837351a399a5e8e95b14987d6ffd9cfb4e81 Mon Sep 17 00:00:00 2001 From: GRayHook Date: Wed, 17 Jun 2026 12:28:22 +0700 Subject: [PATCH] =?UTF-8?q?Phase=209:=20track=20slash=20/skill:name=20on?= =?UTF-8?q?=20input=20hook=20=E2=80=94=20SPEC=20=C2=A76.2=20#1.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire input handler to detect slash skills and upsert into session state using registered skill metadata from before_agent_start cache or loadSkills fallback. Co-authored-by: Cursor --- src/index.ts | 34 +++++++++++++++++++++++++++++++--- src/skills-registry.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 src/skills-registry.ts diff --git a/src/index.ts b/src/index.ts index 58ed041..a44c35e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,35 @@ -import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; +import type { ExtensionAPI, Skill } from "@earendil-works/pi-coding-agent"; +import { detectSlashSkill } from "./detect.js"; +import { findRegisteredSkillByName, resolveRegisteredSkills } from "./skills-registry.js"; +import { createInitialState, trackSkill } from "./state.js"; export default function skillReinject(pi: ExtensionAPI): void { - pi.on("session_start", () => { - // Phase 0 shell — hooks wired in later phases + const state = createInitialState(); + let registeredSkills: Skill[] = []; + + pi.on("before_agent_start", async (event) => { + registeredSkills = event.systemPromptOptions.skills ?? registeredSkills; + }); + + pi.on("input", async (event, ctx) => { + if (event.source === "extension") { + return { action: "continue" }; + } + + const skillName = detectSlashSkill(event.text); + if (skillName) { + const skills = resolveRegisteredSkills(ctx.cwd, registeredSkills); + const skill = findRegisteredSkillByName(skills, skillName); + if (skill) { + trackSkill(state, { + name: skill.name, + filePath: skill.filePath, + baseDir: skill.baseDir, + source: "slash", + }); + } + } + + return { action: "continue" }; }); } diff --git a/src/skills-registry.ts b/src/skills-registry.ts new file mode 100644 index 0000000..2f2e3dc --- /dev/null +++ b/src/skills-registry.ts @@ -0,0 +1,27 @@ +import { getAgentDir, loadSkills, type Skill } from "@earendil-works/pi-coding-agent"; + +/** First match on name collision (SPEC §11). */ +export function findRegisteredSkillByName( + skills: readonly Skill[], + name: string, +): Skill | undefined { + return skills.find((skill) => skill.name === name); +} + +/** Mirror resourceLoader skill list when cache is empty (e.g. first user input). */ +export function loadRegisteredSkills(cwd: string): Skill[] { + return loadSkills({ + cwd, + agentDir: getAgentDir(), + skillPaths: [], + includeDefaults: true, + }).skills; +} + +/** Prefer live skills from before_agent_start; fall back to loadSkills (SPEC §6.2). */ +export function resolveRegisteredSkills(cwd: string, cached: readonly Skill[]): Skill[] { + if (cached.length > 0) { + return [...cached]; + } + return loadRegisteredSkills(cwd); +}