import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "fs"; import { tmpdir } from "os"; import { join } from "path"; import { afterEach, describe, expect, it, vi } from "vitest"; import { reinjectNow, resolveReinjectSkillNames } from "../src/reinject"; import { createDefaultSettings } from "../src/settings"; import { createInitialState, trackSkill } from "../src/state"; const tempDirs: string[] = []; afterEach(() => { for (const dir of tempDirs.splice(0)) { rmSync(dir, { recursive: true, force: true }); } }); function tempSkillDir(name: string): { filePath: string; baseDir: string } { const root = mkdtempSync(join(tmpdir(), "pi-skill-reinject-now-")); tempDirs.push(root); const baseDir = join(root, name); mkdirSync(baseDir, { recursive: true }); const filePath = join(baseDir, "SKILL.md"); writeFileSync(filePath, "# skill\n", "utf8"); return { filePath, baseDir }; } describe("resolveReinjectSkillNames", () => { it("includes loose tracked skill when file exists and requireRegistered is false", () => { const { filePath, baseDir } = tempSkillDir("loose"); const state = createInitialState(); trackSkill(state, { name: "loose", filePath, baseDir, source: "slash" }); expect(resolveReinjectSkillNames(state, createDefaultSettings(), [])).toEqual(["loose"]); }); it("excludes unregistered skills when requireRegistered is true", () => { const { filePath, baseDir } = tempSkillDir("strict"); const state = createInitialState(); trackSkill(state, { name: "strict", filePath, baseDir, source: "slash" }); const settings = createDefaultSettings(); settings.requireRegistered = true; expect(resolveReinjectSkillNames(state, settings, [])).toEqual([]); }); }); describe("reinjectNow loose path", () => { it("sends skill block from disk for unregistered tracked skill", () => { const { filePath, baseDir } = tempSkillDir("loose"); const state = createInitialState(); trackSkill(state, { name: "loose", filePath, baseDir, source: "slash" }); const sendUserMessage = vi.fn(); const pi = { sendUserMessage } as never; const ctx = { hasUI: false, isIdle: () => true } as never; reinjectNow(pi, state, createDefaultSettings(), ctx, []); expect(sendUserMessage).toHaveBeenCalledTimes(1); expect(sendUserMessage.mock.calls[0]?.[0]).toContain('