import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs"; import { tmpdir } from "os"; import { join } from "path"; import { afterEach, describe, expect, it } from "vitest"; import { DEFAULT_SKILL_REINJECT_SETTINGS, SKILL_REINJECT_SETTINGS_KEY, createDefaultSettings, effectiveEnabled, effectiveIntegration, mergeSkillReinjectIntoSettingsFile, mergeSkillReinjectSettings, parseSkillReinjectPartial, } from "../src/settings"; const tempDirs: string[] = []; afterEach(() => { for (const dir of tempDirs.splice(0)) { rmSync(dir, { recursive: true, force: true }); } }); function tempSettingsPath(): string { const dir = mkdtempSync(join(tmpdir(), "pi-skill-reinject-settings-")); tempDirs.push(dir); return join(dir, "settings.json"); } describe("createDefaultSettings", () => { it("matches SPEC ยง7.3 defaults", () => { expect(createDefaultSettings()).toEqual(DEFAULT_SKILL_REINJECT_SETTINGS); }); }); describe("parseSkillReinjectPartial", () => { it("ignores invalid shapes and fields", () => { expect(parseSkillReinjectPartial(null)).toEqual({}); expect(parseSkillReinjectPartial([])).toEqual({}); expect( parseSkillReinjectPartial({ enabled: "yes", autoCompactIntegration: "bogus", extra: true, }), ).toEqual({}); }); it("keeps valid fields only", () => { expect( parseSkillReinjectPartial({ enabled: true, suffix: "custom", }), ).toEqual({ enabled: true, suffix: "custom" }); }); it("parses debug flag", () => { expect(parseSkillReinjectPartial({ debug: true })).toEqual({ debug: true }); }); it("parses requireRegistered flag", () => { expect(parseSkillReinjectPartial({ requireRegistered: true })).toEqual({ requireRegistered: true, }); }); }); describe("mergeSkillReinjectSettings", () => { it("applies global then project overrides on defaults", () => { expect( mergeSkillReinjectSettings({ enabled: true }, { trackReadPaths: false }), ).toEqual({ ...DEFAULT_SKILL_REINJECT_SETTINGS, enabled: true, trackReadPaths: false, }); }); it("lets project win over global for the same field", () => { expect( mergeSkillReinjectSettings({ enabled: true }, { enabled: false }), ).toEqual({ ...DEFAULT_SKILL_REINJECT_SETTINGS, enabled: false, }); }); }); describe("mergeSkillReinjectIntoSettingsFile", () => { it("merges skillReinject fields across successive writes", () => { const path = tempSettingsPath(); mergeSkillReinjectIntoSettingsFile(path, { enabled: true }); mergeSkillReinjectIntoSettingsFile(path, { triggerTurn: true }); const saved = JSON.parse(readFileSync(path, "utf8")) as Record; expect(saved[SKILL_REINJECT_SETTINGS_KEY]).toEqual({ enabled: true, triggerTurn: true, }); }); it("preserves existing top-level keys", () => { const path = tempSettingsPath(); writeFileSync( path, `${JSON.stringify({ theme: "light", compaction: { enabled: false } }, null, 2)}\n`, "utf8", ); mergeSkillReinjectIntoSettingsFile(path, { enabled: true, suffix: "x" }); const saved = JSON.parse(readFileSync(path, "utf8")) as Record; expect(saved.theme).toBe("light"); expect(saved.compaction).toEqual({ enabled: false }); expect(saved[SKILL_REINJECT_SETTINGS_KEY]).toEqual({ enabled: true, suffix: "x", }); }); }); describe("effectiveEnabled", () => { const settings = createDefaultSettings(); it("uses session override when set", () => { expect(effectiveEnabled(true, settings)).toBe(true); expect(effectiveEnabled(false, { ...settings, enabled: true })).toBe(false); }); it("falls back to global enabled when override is null", () => { expect(effectiveEnabled(null, settings)).toBe(false); expect(effectiveEnabled(null, { ...settings, enabled: true })).toBe(true); }); }); describe("effectiveIntegration", () => { const base = createDefaultSettings(); it("honors explicit defer and immediate", () => { expect( effectiveIntegration({ ...base, autoCompactIntegration: "defer" }, false), ).toBe("defer"); expect( effectiveIntegration({ ...base, autoCompactIntegration: "immediate" }, true), ).toBe("immediate"); }); it("uses triggerTurn when integration is off", () => { expect( effectiveIntegration({ ...base, autoCompactIntegration: "off", triggerTurn: false }, true), ).toBe("defer"); expect( effectiveIntegration({ ...base, autoCompactIntegration: "off", triggerTurn: true }, true), ).toBe("immediate"); }); it("defers when auto mode detects pi-auto-compact", () => { expect(effectiveIntegration(base, true)).toBe("defer"); }); it("uses triggerTurn in auto mode without pi-auto-compact", () => { expect(effectiveIntegration({ ...base, triggerTurn: false }, false)).toBe("defer"); expect(effectiveIntegration({ ...base, triggerTurn: true }, false)).toBe("immediate"); }); it("prefers session integration override", () => { expect( effectiveIntegration({ ...base, autoCompactIntegration: "immediate" }, false, "defer"), ).toBe("defer"); }); });