Packages

A Gleam library for building and orchestrating agents on the BEAM.

Current section

Files

Jump to
pig src pig@skill.erl
Raw

src/pig@skill.erl

-module(pig@skill).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/pig/skill.gleam").
-export([parse_frontmatter/1, load/1, skill_to_system_fragment/1]).
-export_type([skill/0, skill_error/0, frontmatter/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Skill loading and parsing.\n"
"\n"
" A Skill is a directory on disk containing a `SKILL.md` file with\n"
" YAML frontmatter (name + description) and a markdown body.\n"
"\n"
" Frontmatter parsing is deliberately simple: split on `---` delimiters,\n"
" extract `name` and `description` fields from the YAML-like block.\n"
" No full YAML parser needed.\n"
).
-type skill() :: {skill, binary(), binary(), binary(), list(binary())}.
-type skill_error() :: skill_not_found | invalid_frontmatter.
-type frontmatter() :: {frontmatter, binary(), binary(), binary()}.
-file("src/pig/skill.gleam", 126).
?DOC(
" List supplementary files in a skill directory (excluding SKILL.md).\n"
" Returns relative paths from the skill directory.\n"
).
-spec list_files(binary()) -> list(binary()).
list_files(Path) ->
case simplifile_erl:read_directory(Path) of
{ok, Entries} ->
_pipe = Entries,
_pipe@1 = gleam@list:filter(
_pipe,
fun(E) -> E /= <<"SKILL.md"/utf8>> end
),
gleam@list:filter(
_pipe@1,
fun(E@1) ->
Full = <<<<Path/binary, "/"/utf8>>/binary, E@1/binary>>,
case simplifile_erl:is_file(Full) of
{ok, true} ->
true;
_ ->
false
end
end
);
{error, _} ->
[]
end.
-file("src/pig/skill.gleam", 69).
?DOC(" Walk frontmatter lines collecting name/description accumulators.\n").
-spec parse_lines(list(binary()), binary(), binary(), boolean()) -> {ok,
frontmatter()} |
{error, skill_error()}.
parse_lines(Lines, Acc_name, Acc_desc, In_fm) ->
case Lines of
[] ->
{error, invalid_frontmatter};
[<<"---"/utf8>> | Rest] when In_fm ->
Body = gleam@string:join(Rest, <<"\n"/utf8>>),
case {Acc_name, Acc_desc} of
{<<""/utf8>>, _} ->
{error, invalid_frontmatter};
{_, <<""/utf8>>} ->
{error, invalid_frontmatter};
{_, _} ->
{ok,
{frontmatter,
gleam@string:trim(Acc_name),
gleam@string:trim(Acc_desc),
Body}}
end;
[Line | Rest@1] when In_fm ->
Parts = gleam@string:split(Line, <<": "/utf8>>),
case Parts of
[Key, Value] ->
New_name = case Key of
<<"name"/utf8>> ->
Value;
_ ->
Acc_name
end,
New_desc = case Key of
<<"description"/utf8>> ->
Value;
_ ->
Acc_desc
end,
parse_lines(Rest@1, New_name, New_desc, true);
_ ->
parse_lines(Rest@1, Acc_name, Acc_desc, true)
end;
_ ->
{error, invalid_frontmatter}
end.
-file("src/pig/skill.gleam", 61).
?DOC(
" Parse frontmatter from a SKILL.md content string.\n"
"\n"
" Expects `---\\n` at start, content between delimiters, and closing `---\\n`.\n"
" Extracts `name:` and `description:` fields. Returns remaining body after\n"
" the closing delimiter.\n"
"\n"
" This is a simple parser — not a full YAML parser. It handles:\n"
" - `key: value` lines\n"
).
-spec parse_frontmatter(binary()) -> {ok, frontmatter()} |
{error, skill_error()}.
parse_frontmatter(Raw) ->
case gleam@string:split(Raw, <<"\n"/utf8>>) of
[<<"---"/utf8>> | Rest] ->
parse_lines(Rest, <<""/utf8>>, <<""/utf8>>, true);
_ ->
{error, invalid_frontmatter}
end.
-file("src/pig/skill.gleam", 34).
?DOC(
" Load a skill from a directory path.\n"
"\n"
" Reads `SKILL.md` from the directory, parses frontmatter for name/description,\n"
" and lists supplementary files (everything except `SKILL.md`).\n"
).
-spec load(binary()) -> {ok, skill()} | {error, skill_error()}.
load(Path) ->
Skill_md_path = <<Path/binary, "/SKILL.md"/utf8>>,
case simplifile:read(Skill_md_path) of
{ok, Content} ->
case parse_frontmatter(Content) of
{ok, Fm} ->
{ok,
{skill,
erlang:element(2, Fm),
erlang:element(3, Fm),
Path,
list_files(Path)}};
{error, E} ->
{error, E}
end;
{error, _} ->
{error, skill_not_found}
end.
-file("src/pig/skill.gleam", 114).
?DOC(
" Generate a system prompt fragment from a skill.\n"
"\n"
" Used to inject skill descriptions into the system prompt so the agent\n"
" knows what skills are available.\n"
).
-spec skill_to_system_fragment(skill()) -> binary().
skill_to_system_fragment(Skill) ->
<<<<<<<<<<<<"Skill: "/utf8, (erlang:element(2, Skill))/binary>>/binary,
"\nDescription: "/utf8>>/binary,
(erlang:element(3, Skill))/binary>>/binary,
"\nPath: "/utf8>>/binary,
(erlang:element(4, Skill))/binary>>/binary,
"\n"/utf8>>.