diff --git a/src/server/execute.test.ts b/src/server/execute.test.ts index 4970481..1aefa30 100644 --- a/src/server/execute.test.ts +++ b/src/server/execute.test.ts @@ -1167,6 +1167,62 @@ describe("execute: happy path", () => { expect(result.exitCode).toBe(0); }); + + it("logs bundled skill names and count (FAR-36 diagnostic)", async () => { + const skills = [ + { key: "safety--abc123", runtimeName: "safety--abc123", desired: true, managed: true, required: true, state: "configured" as const }, + { key: "sdlc--def456", runtimeName: "sdlc--def456", desired: true, managed: true, required: true, state: "configured" as const }, + ]; + mockReadSkillEntries.mockResolvedValue(skills); + + const logs: Array<{ stream: string; msg: string }> = []; + const onLog = vi.fn().mockImplementation(async (stream: string, msg: string) => { logs.push({ stream, msg }); }); + + const executePromise = execute(makeCtx({ onLog } as Partial)); + await vi.advanceTimersByTimeAsync(3_100); + await executePromise; + + const skillLine = logs.find((l) => l.msg.includes("Skills bundled")); + expect(skillLine).toBeDefined(); + expect(skillLine?.stream).toBe("stdout"); + expect(skillLine?.msg).toContain("(2):"); + expect(skillLine?.msg).toContain("safety--abc123"); + expect(skillLine?.msg).toContain("sdlc--def456"); + }); + + it("logs Skills bundled (0): none when no skills are configured (FAR-36 diagnostic)", async () => { + mockReadSkillEntries.mockResolvedValue([]); + + const logs: Array<{ stream: string; msg: string }> = []; + const onLog = vi.fn().mockImplementation(async (stream: string, msg: string) => { logs.push({ stream, msg }); }); + + const executePromise = execute(makeCtx({ onLog } as Partial)); + await vi.advanceTimersByTimeAsync(3_100); + await executePromise; + + const skillLine = logs.find((l) => l.msg.includes("Skills bundled")); + expect(skillLine).toBeDefined(); + expect(skillLine?.msg).toContain("(0): none"); + }); + + it("includes skill count in onMeta commandNotes (FAR-36 diagnostic)", async () => { + const skills = [ + { key: "safety--abc123", runtimeName: "safety--abc123", desired: true, managed: true, required: true, state: "configured" as const }, + ]; + mockReadSkillEntries.mockResolvedValue(skills); + + const onMeta = vi.fn().mockResolvedValue(undefined); + const executePromise = execute(makeCtx({ onMeta } as Partial)); + await vi.advanceTimersByTimeAsync(3_100); + await executePromise; + + expect(onMeta).toHaveBeenCalled(); + const notes: string[] = onMeta.mock.calls[0][0].commandNotes; + const skillsNote = notes.find((n: string) => n.startsWith("Skills")); + expect(skillsNote).toBeDefined(); + expect(skillsNote).toContain("(1):"); + expect(skillsNote).toContain("safety--abc123"); + }); }); // ─── execute: waitForPod edge cases ────────────────────────────────────────── diff --git a/src/server/execute.ts b/src/server/execute.ts index 1e5c8b6..4920109 100644 --- a/src/server/execute.ts +++ b/src/server/execute.ts @@ -796,6 +796,8 @@ export async function execute(ctx: AdapterExecutionContext): Promise desiredSkillNames.has(e.key)); + const skillSummary = desiredSkills.length > 0 ? desiredSkills.map((s) => s.runtimeName ?? s.key).join(", ") : "none"; + await onLog("stdout", `[paperclip] Skills bundled (${desiredSkills.length}): ${skillSummary}\n`); const instructionsFilePath = asString(config.instructionsFilePath, "").trim(); const instructionsFileDir = instructionsFilePath ? `${path.dirname(instructionsFilePath)}/` : ""; let instructionsContents: string | null = null; @@ -892,6 +894,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise