fix(category-skill-reminder): dynamically include available skills with user priority

This commit is contained in:
YeonGyu-Kim
2026-02-06 17:36:40 +09:00
parent 472550b411
commit b81e483d9d
3 changed files with 133 additions and 44 deletions
+87 -15
View File
@@ -1,6 +1,7 @@
import { describe, expect, test, beforeEach, afterEach, spyOn } from "bun:test"
import { createCategorySkillReminderHook } from "./index"
import { updateSessionAgent, clearSessionAgent, _resetForTesting } from "../../features/claude-code-session-state"
import type { AvailableSkill } from "../../agents/dynamic-agent-prompt-builder"
import * as sharedModule from "../../shared"
describe("category-skill-reminder hook", () => {
@@ -29,10 +30,14 @@ describe("category-skill-reminder hook", () => {
} as any
}
function createHook(availableSkills: AvailableSkill[] = []) {
return createCategorySkillReminderHook(createMockPluginInput(), availableSkills)
}
describe("target agent detection", () => {
test("should inject reminder for sisyphus agent after 3 tool calls", async () => {
// given - sisyphus agent session with multiple tool calls
const hook = createCategorySkillReminderHook(createMockPluginInput())
const hook = createHook()
const sessionID = "sisyphus-session"
updateSessionAgent(sessionID, "Sisyphus")
@@ -52,7 +57,7 @@ describe("category-skill-reminder hook", () => {
test("should inject reminder for atlas agent", async () => {
// given - atlas agent session
const hook = createCategorySkillReminderHook(createMockPluginInput())
const hook = createHook()
const sessionID = "atlas-session"
updateSessionAgent(sessionID, "Atlas")
@@ -71,7 +76,7 @@ describe("category-skill-reminder hook", () => {
test("should inject reminder for sisyphus-junior agent", async () => {
// given - sisyphus-junior agent session
const hook = createCategorySkillReminderHook(createMockPluginInput())
const hook = createHook()
const sessionID = "junior-session"
updateSessionAgent(sessionID, "sisyphus-junior")
@@ -90,7 +95,7 @@ describe("category-skill-reminder hook", () => {
test("should NOT inject reminder for non-target agents", async () => {
// given - librarian agent session (not a target)
const hook = createCategorySkillReminderHook(createMockPluginInput())
const hook = createHook()
const sessionID = "librarian-session"
updateSessionAgent(sessionID, "librarian")
@@ -109,7 +114,7 @@ describe("category-skill-reminder hook", () => {
test("should detect agent from input.agent when session state is empty", async () => {
// given - no session state, agent provided in input
const hook = createCategorySkillReminderHook(createMockPluginInput())
const hook = createHook()
const sessionID = "input-agent-session"
const output = { title: "", output: "result", metadata: {} }
@@ -127,7 +132,7 @@ describe("category-skill-reminder hook", () => {
describe("delegation tool tracking", () => {
test("should NOT inject reminder if delegate_task is used", async () => {
// given - sisyphus agent that uses delegate_task
const hook = createCategorySkillReminderHook(createMockPluginInput())
const hook = createHook()
const sessionID = "delegation-session"
updateSessionAgent(sessionID, "Sisyphus")
@@ -147,7 +152,7 @@ describe("category-skill-reminder hook", () => {
test("should NOT inject reminder if call_omo_agent is used", async () => {
// given - sisyphus agent that uses call_omo_agent
const hook = createCategorySkillReminderHook(createMockPluginInput())
const hook = createHook()
const sessionID = "omo-agent-session"
updateSessionAgent(sessionID, "Sisyphus")
@@ -167,7 +172,7 @@ describe("category-skill-reminder hook", () => {
test("should NOT inject reminder if task tool is used", async () => {
// given - sisyphus agent that uses task tool
const hook = createCategorySkillReminderHook(createMockPluginInput())
const hook = createHook()
const sessionID = "task-session"
updateSessionAgent(sessionID, "Sisyphus")
@@ -189,7 +194,7 @@ describe("category-skill-reminder hook", () => {
describe("tool call counting", () => {
test("should NOT inject reminder before 3 tool calls", async () => {
// given - sisyphus agent with only 2 tool calls
const hook = createCategorySkillReminderHook(createMockPluginInput())
const hook = createHook()
const sessionID = "few-calls-session"
updateSessionAgent(sessionID, "Sisyphus")
@@ -207,7 +212,7 @@ describe("category-skill-reminder hook", () => {
test("should only inject reminder once per session", async () => {
// given - sisyphus agent session
const hook = createCategorySkillReminderHook(createMockPluginInput())
const hook = createHook()
const sessionID = "once-session"
updateSessionAgent(sessionID, "Sisyphus")
@@ -231,7 +236,7 @@ describe("category-skill-reminder hook", () => {
test("should only count delegatable work tools", async () => {
// given - sisyphus agent with mixed tool calls
const hook = createCategorySkillReminderHook(createMockPluginInput())
const hook = createHook()
const sessionID = "mixed-tools-session"
updateSessionAgent(sessionID, "Sisyphus")
@@ -252,7 +257,7 @@ describe("category-skill-reminder hook", () => {
describe("event handling", () => {
test("should reset state on session.deleted event", async () => {
// given - sisyphus agent with reminder already shown
const hook = createCategorySkillReminderHook(createMockPluginInput())
const hook = createHook()
const sessionID = "delete-session"
updateSessionAgent(sessionID, "Sisyphus")
@@ -278,7 +283,7 @@ describe("category-skill-reminder hook", () => {
test("should reset state on session.compacted event", async () => {
// given - sisyphus agent with reminder already shown
const hook = createCategorySkillReminderHook(createMockPluginInput())
const hook = createHook()
const sessionID = "compact-session"
updateSessionAgent(sessionID, "Sisyphus")
@@ -306,7 +311,7 @@ describe("category-skill-reminder hook", () => {
describe("case insensitivity", () => {
test("should handle tool names case-insensitively", async () => {
// given - sisyphus agent with mixed case tool names
const hook = createCategorySkillReminderHook(createMockPluginInput())
const hook = createHook()
const sessionID = "case-session"
updateSessionAgent(sessionID, "Sisyphus")
@@ -325,7 +330,7 @@ describe("category-skill-reminder hook", () => {
test("should handle delegation tool names case-insensitively", async () => {
// given - sisyphus agent using DELEGATE_TASK in uppercase
const hook = createCategorySkillReminderHook(createMockPluginInput())
const hook = createHook()
const sessionID = "case-delegate-session"
updateSessionAgent(sessionID, "Sisyphus")
@@ -343,4 +348,71 @@ describe("category-skill-reminder hook", () => {
clearSessionAgent(sessionID)
})
})
describe("dynamic skills reminder message", () => {
test("shows built-in skills when only built-in skills are available", async () => {
// given
const availableSkills: AvailableSkill[] = [
{ name: "frontend-ui-ux", description: "Frontend UI/UX work", location: "plugin" },
{ name: "git-master", description: "Git operations", location: "plugin" },
{ name: "playwright", description: "Browser automation", location: "plugin" },
]
const hook = createHook(availableSkills)
const sessionID = "builtins-only"
updateSessionAgent(sessionID, "Sisyphus")
const output = { title: "", output: "result", metadata: {} }
// when
await hook["tool.execute.after"]({ tool: "edit", sessionID, callID: "1" }, output)
await hook["tool.execute.after"]({ tool: "edit", sessionID, callID: "2" }, output)
await hook["tool.execute.after"]({ tool: "edit", sessionID, callID: "3" }, output)
// then
expect(output.output).toContain("**Built-in**:")
expect(output.output).toContain("frontend-ui-ux")
expect(output.output).toContain("**⚡ YOUR SKILLS (PRIORITY)**")
expect(output.output).toContain("load_skills=[\"frontend-ui-ux\"")
})
test("emphasizes user skills with PRIORITY and uses first user skill in example", async () => {
// given
const availableSkills: AvailableSkill[] = [
{ name: "frontend-ui-ux", description: "Frontend UI/UX work", location: "plugin" },
{ name: "react-19", description: "React 19 expertise", location: "user" },
{ name: "web-designer", description: "Visual design", location: "user" },
]
const hook = createHook(availableSkills)
const sessionID = "user-skills"
updateSessionAgent(sessionID, "Atlas")
const output = { title: "", output: "result", metadata: {} }
// when
await hook["tool.execute.after"]({ tool: "bash", sessionID, callID: "1" }, output)
await hook["tool.execute.after"]({ tool: "bash", sessionID, callID: "2" }, output)
await hook["tool.execute.after"]({ tool: "bash", sessionID, callID: "3" }, output)
// then
expect(output.output).toContain("**⚡ YOUR SKILLS (PRIORITY)**")
expect(output.output).toContain("react-19")
expect(output.output).toContain("> User-installed skills OVERRIDE")
expect(output.output).toContain("load_skills=[\"react-19\"")
})
test("still injects a generic reminder when no skills are provided", async () => {
// given
const hook = createHook([])
const sessionID = "no-skills"
updateSessionAgent(sessionID, "Sisyphus")
const output = { title: "", output: "result", metadata: {} }
// when
await hook["tool.execute.after"]({ tool: "read", sessionID, callID: "1" }, output)
await hook["tool.execute.after"]({ tool: "read", sessionID, callID: "2" }, output)
await hook["tool.execute.after"]({ tool: "read", sessionID, callID: "3" }, output)
// then
expect(output.output).toContain("[Category+Skill Reminder]")
expect(output.output).toContain("load_skills=[]")
})
})
})