Skip to main content

lib/mix/tasks/skill_kit.demo.ex

defmodule Mix.Tasks.SkillKit.Demo do
  @moduledoc """
  Smoke-test the full SkillKit agent loop against the Anthropic API.

      mix skill_kit.demo "What is 2 + 2?"
  """

  use Mix.Task

  alias SkillKit.Event.Delta
  alias SkillKit.Event.Error, as: EventError
  alias SkillKit.Types.AssistantMessage

  @shortdoc "Run a single-turn agent conversation"
  @idle_timeout 10_000

  @impl true
  def run(args) do
    Mix.Task.run("app.start")

    prompt = Enum.join(args, " ")

    if prompt == "" do
      Mix.shell().error("Usage: mix skill_kit.demo \"your prompt here\"")
      exit({:shutdown, 1})
    end

    agent_dir = Path.join(System.get_env("SKILL_KIT_AGENTS", "examples/agents"), "neve")
    skills_dir = System.get_env("SKILL_KIT_SKILLS", "examples/skills")

    {:ok, agent} =
      SkillKit.start_agent(agent_dir,
        tools: [{SkillKit.Tools.Shell, []}],
        skills: [{SkillKit.Kit.Local, dir: skills_dir}],
        caller: self()
      )

    Mix.shell().info("Sent: #{prompt}")
    :ok = SkillKit.send_message(agent, prompt)

    agent.name
    |> SkillKit.Stream.stream(timeout: @idle_timeout)
    |> Stream.each(&print/1)
    |> Enum.reduce(:timeout, fn event, _ -> event end)
    |> report()

    SkillKit.stop_agent(agent)
  end

  defp print(%Delta{text: text}), do: IO.write(text)
  defp print(_event), do: :ok

  defp report(%AssistantMessage{}), do: IO.puts("--- Turn complete ---")
  defp report(%EventError{reason: reason}), do: Mix.shell().error("\nError: #{inspect(reason)}")
  defp report(:timeout), do: :ok
end