Files
pi-auto-reinject/test/reinject-deferred-consume.test.ts
T
grayhook ebc169c91f Phase 14: build reinject blocks from tracked paths — loose skill disk fallback
buildReinjectBlocks uses tracked filePath/baseDir when resourceLoader has no entry and requireRegistered is false, completing defer-path B-002 injection.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 17:34:43 +07:00

112 lines
3.7 KiB
TypeScript

import { existsSync, mkdtempSync, mkdirSync, rmSync, writeFileSync } from "fs";
import { tmpdir } from "os";
import { join } from "path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { filterPendingReinjectForConsume, tryConsumeDeferredReinject } 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-consume-"));
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("filterPendingReinjectForConsume", () => {
it("keeps registered pending skills", () => {
const state = createInitialState();
trackSkill(state, {
name: "alpha",
filePath: "/skills/alpha/SKILL.md",
baseDir: "/skills/alpha",
source: "slash",
});
expect(
filterPendingReinjectForConsume(
["alpha"],
state,
createDefaultSettings(),
[{ name: "alpha" }],
),
).toEqual(["alpha"]);
});
it("includes unregistered skill from disk when requireRegistered is false", () => {
const { filePath, baseDir } = tempSkillDir("loose");
const state = createInitialState();
trackSkill(state, { name: "loose", filePath, baseDir, source: "slash" });
const notify = vi.fn();
const ctx = { hasUI: true, ui: { notify } } as never;
expect(
filterPendingReinjectForConsume(["loose"], state, createDefaultSettings(), [], ctx),
).toEqual(["loose"]);
expect(notify).toHaveBeenCalledWith('skill-reinject: re-injected "loose" from disk', "info");
});
it("skips unregistered skill when requireRegistered is true", () => {
const { filePath, baseDir } = tempSkillDir("strict");
const state = createInitialState();
trackSkill(state, { name: "strict", filePath, baseDir, source: "slash" });
const notify = vi.fn();
const ctx = { hasUI: true, ui: { notify } } as never;
const settings = createDefaultSettings();
settings.requireRegistered = true;
expect(filterPendingReinjectForConsume(["strict"], state, settings, [], ctx)).toEqual([]);
expect(notify).toHaveBeenCalledWith(
'skill-reinject: skipped "strict" — no longer registered',
"warning",
);
});
it("skips when tracked file is missing on disk", () => {
const state = createInitialState();
trackSkill(state, {
name: "missing",
filePath: "/no/such/SKILL.md",
baseDir: "/no/such",
source: "slash",
});
expect(existsSync("/no/such/SKILL.md")).toBe(false);
const notify = vi.fn();
const ctx = { hasUI: true, ui: { notify } } as never;
expect(
filterPendingReinjectForConsume(["missing"], state, createDefaultSettings(), [], ctx),
).toEqual([]);
expect(notify).toHaveBeenCalledWith(
'skill-reinject: skipped "missing" — SKILL.md not found on disk',
"warning",
);
});
});
describe("tryConsumeDeferredReinject loose path", () => {
it("injects skill block from tracked filePath when not registered", () => {
const { filePath, baseDir } = tempSkillDir("loose");
const state = createInitialState();
trackSkill(state, { name: "loose", filePath, baseDir, source: "slash" });
state.pendingReinject = ["loose"];
const result = tryConsumeDeferredReinject(state, createDefaultSettings(), [], undefined);
expect(result?.message?.content).toContain('<skill name="loose"');
expect(result?.message?.content).toContain("[skill-reinject] Re-applied after compaction.");
expect(state.pendingReinject).toEqual([]);
});
});