import { describe, expect, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Sink from "effect/Sink"; import * as Stream from "effect/Stream"; import { ChildProcessSpawner } from "effect/unstable/process"; import { discoverGrokSkills, parseGrokInspectSkills } from "./GrokSkills.ts"; const inspectPayload = (skills: ReadonlyArray) => JSON.stringify({ skills }); describe("parseGrokInspectSkills", () => { it("maps inspect entries onto provider skills, sorted by name", () => { const skills = parseGrokInspectSkills( inspectPayload([ { name: "writing-docs", description: "Write user docs.", source: { type: "user", path: "/home/dev/.grok/skills/writing-docs/SKILL.md" }, userInvocable: true, }, { name: "deploy", description: "Deploy the app.", source: { type: "plugin", path: "/home/dev/.grok/installed-plugins/pkg/plug/skills/deploy/SKILL.md", }, userInvocable: true, }, ]), ); expect(skills).toEqual([ { name: "deploy", description: "Deploy the app.", path: "/home/dev/.grok/installed-plugins/pkg/plug/skills/deploy/SKILL.md", scope: "plugin", enabled: true, }, { name: "writing-docs", description: "Write user docs.", path: "/home/dev/.grok/skills/writing-docs/SKILL.md", scope: "user", enabled: true, }, ]); }); it("disables skills the CLI marks as not user-invocable", () => { const skills = parseGrokInspectSkills( inspectPayload([ { name: "internal-helper", source: { type: "bundled", path: "/opt/grok/bundled/skills/internal-helper/SKILL.md" }, userInvocable: false, }, ]), ); expect(skills).toEqual([ { name: "internal-helper", path: "/opt/grok/bundled/skills/internal-helper/SKILL.md", scope: "bundled", enabled: false, }, ]); }); it("skips entries without a name or a filesystem path", () => { const skills = parseGrokInspectSkills( inspectPayload([ { name: " ", source: { type: "user", path: "/tmp/skills/a/SKILL.md" } }, { name: "no-path", source: { type: "user" } }, { name: "no-source" }, "not-an-object", { name: "kept", source: { type: "project", path: "/repo/.grok/skills/kept/SKILL.md" } }, ]), ); expect(skills.map((skill) => skill.name)).toEqual(["kept"]); }); it("returns an empty list for malformed or unexpected output", () => { expect(parseGrokInspectSkills("not json")).toEqual([]); expect(parseGrokInspectSkills("null")).toEqual([]); expect(parseGrokInspectSkills(JSON.stringify({ skills: "nope" }))).toEqual([]); expect(parseGrokInspectSkills(JSON.stringify({}))).toEqual([]); }); }); describe("discoverGrokSkills", () => { it.effect("spawns the inspect probe in the configured cwd", () => { const spawnCwds: Array = []; const spawner = ChildProcessSpawner.make((command) => { spawnCwds.push(command._tag === "StandardCommand" ? command.options.cwd : undefined); return Effect.succeed( ChildProcessSpawner.makeHandle({ pid: ChildProcessSpawner.ProcessId(1), exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(0)), isRunning: Effect.succeed(false), kill: () => Effect.void, unref: Effect.succeed(Effect.void), stdin: Sink.drain, stdout: Stream.encodeText( Stream.make( inspectPayload([ { name: "kept", source: { type: "project", path: "/workspaces/demo/.grok/skills/kept/SKILL.md" }, }, ]), ), ), stderr: Stream.empty, all: Stream.empty, getInputFd: () => Sink.drain, getOutputFd: () => Stream.empty, }), ); }); return Effect.gen(function* () { const skills = yield* discoverGrokSkills({ binaryPath: "grok" }, {}, "/workspaces/demo").pipe( Effect.provide(Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner)), ); expect(spawnCwds).toEqual(["/workspaces/demo"]); expect(skills.map((skill) => skill.name)).toEqual(["kept"]); }); }); });