diff --git a/lib/mix/tasks/usage_rules.sync.ex b/lib/mix/tasks/usage_rules.sync.ex index 34ccd29..1fd86bb 100644 --- a/lib/mix/tasks/usage_rules.sync.ex +++ b/lib/mix/tasks/usage_rules.sync.ex @@ -333,7 +333,8 @@ if Code.ensure_loaded?(Igniter) do if igniter.assigns[:test_mode?] do igniter.rewrite.sources |> Enum.filter(fn {path, _source} -> - String.match?(path, ~r|^deps/[^/]+/usage-rules\.md$|) || + String.match?(path, ~r|^deps/[^/]+/mix\.exs$|) || + String.match?(path, ~r|^deps/[^/]+/usage-rules\.md$|) || String.match?(path, ~r|^deps/[^/]+/usage-rules/[^/]+\.md$|) || String.match?(path, ~r|^deps/[^/]+/usage-rules/skills/[^/]+/SKILL\.md$|) || String.match?(path, ~r|^deps/[^/]+/usage-rules/skills/[^/]+/.+$|) @@ -353,14 +354,18 @@ if Code.ensure_loaded?(Igniter) do defp get_packages_with_usage_rules(igniter, all_deps) do Enum.filter(all_deps, fn {_name, path} when is_binary(path) and path != "" -> - Igniter.exists?(igniter, Path.join(path, "usage-rules.md")) || - Igniter.exists?(igniter, Path.join(path, "usage-rules")) + package_has_usage_rules?(igniter, path) _ -> false end) end + defp package_has_usage_rules?(igniter, package_path) do + Igniter.exists?(igniter, Path.join(package_path, "usage-rules.md")) || + Enum.any?(find_available_sub_rules(igniter, package_path)) + end + # ------------------------------------------------------------------- # Config resolution # ------------------------------------------------------------------- @@ -801,7 +806,12 @@ if Code.ensure_loaded?(Igniter) do custom_description = skill_opts[:description] # Resolve which packages to include in this skill (supports atoms and regexes) - resolved_packages = expand_dep_specs(usage_rule_specs, all_deps) + resolved_packages = + usage_rule_specs + |> expand_dep_specs(all_deps) + |> Enum.filter(fn {_pkg_name, package_path, _mode} -> + package_has_usage_rules?(igniter, package_path) + end) if Enum.any?(resolved_packages) do generate_built_skill( diff --git a/test/mix/tasks/usage_rules.sync_test.exs b/test/mix/tasks/usage_rules.sync_test.exs index bf66cb8..5aaf86a 100644 --- a/test/mix/tasks/usage_rules.sync_test.exs +++ b/test/mix/tasks/usage_rules.sync_test.exs @@ -928,6 +928,36 @@ defmodule Mix.Tasks.UsageRules.SyncTest do refute content =~ "Req" end + test "regex build skips deps without usage rules when rendering references and search docs" do + igniter = + project_with_deps(%{ + "deps/phoenix/usage-rules/ecto.md" => "# Phoenix Ecto", + "deps/phoenix/usage-rules/liveview.md" => "# Phoenix LiveView", + "deps/phoenix_ecto/mix.exs" => "defmodule PhoenixEcto.MixProject, do: nil", + "deps/phoenix_html/mix.exs" => "defmodule PhoenixHTML.MixProject, do: nil" + }) + |> sync( + skills: [ + location: ".claude/skills", + build: [ + "phoenix-framework": [usage_rules: [:phoenix, ~r/^phoenix_/]] + ] + ] + ) + |> assert_creates(".claude/skills/phoenix-framework/SKILL.md") + |> assert_creates(".claude/skills/phoenix-framework/references/ecto.md") + |> assert_creates(".claude/skills/phoenix-framework/references/liveview.md") + + content = file_content(igniter, ".claude/skills/phoenix-framework/SKILL.md") + + assert content =~ "[ecto](references/ecto.md)" + assert content =~ "[liveview](references/liveview.md)" + refute content =~ "references/phoenix_ecto.md" + refute content =~ "references/phoenix_html.md" + refute content =~ "-p phoenix_ecto" + refute content =~ "-p phoenix_html" + end + test "removes stale managed skills no longer in build list" do stale_skill_md = "---\nname: use-old\nmetadata:\n managed-by: usage-rules\n---\n\n\nOld skill.\n"