Packages
A config-driven dev tool for Elixir projects to manage AGENTS.md files and agent skills from dependencies
Current section
42 Versions
Jump to
Current section
42 Versions
Compare versions
5
files changed
+47
additions
-78
deletions
| @@ -12,6 +12,15 @@ See [Conventional Commits](Https://conventionalcommits.org) for commit guideline | |
| 12 12 | |
| 13 13 | <!-- changelog --> |
| 14 14 | |
| 15 | + ## v1.0.0-rc.3 (2026-02-10) |
| 16 | + |
| 17 | + |
| 18 | + |
| 19 | + |
| 20 | + ### Improvements: |
| 21 | + |
| 22 | + * use refernece files for all usage rules by Zach Daniel |
| 23 | + |
| 15 24 | ## v1.0.0-rc.2 (2026-02-07) |
| @@ -122,15 +122,12 @@ defp usage_rules do | |
| 122 122 | deps: [:ash, :req], |
| 123 123 | # Supports regex for matching multiple deps: |
| 124 124 | # deps: [~r/^ash_/], |
| 125 | - # Use {:dep, :reference} to build as reference files instead of inlining: |
| 126 | - # deps: [:ash, {:req, :reference}, {~r/^ash_/, :reference}], |
| 127 125 | |
| 128 126 | # Compose custom skills from multiple packages |
| 129 127 | build: [ |
| 130 128 | "ash-framework": [ |
| 131 129 | description: "Expert on the Ash Framework ecosystem.", |
| 132 | - # Use {:dep, :reference} or {~r/.../, :reference} for reference files |
| 133 | - usage_rules: [:ash, {:ash_postgres, :reference}, {~r/^ash_phoenix/, :reference}] |
| 130 | + usage_rules: [:ash, ~r/^ash_/] |
| 134 131 | ] |
| 135 132 | ] |
| 136 133 | ] |
| @@ -164,7 +161,7 @@ Each entry in the `usage_rules` list can be: | |
| 164 161 | | Option | Type | Description | |
| 165 162 | |--------|------|-------------| |
| 166 163 | | `location` | `string` | Output directory for skills (default: `".claude/skills"`) | |
| 167 | - | `deps` | `list` | Auto-build a `use-<pkg>` skill per listed dependency. Supports atoms, regexes, and `{spec, :reference}` tuples | |
| 164 | + | `deps` | `list` | Auto-build a `use-<pkg>` skill per listed dependency. Supports atoms and regexes | |
| 168 165 | | `build` | `keyword` | Define custom composed skills from multiple packages' usage rules | |
| 169 166 | |
| 170 167 | ## Usage Rules |
| @@ -257,15 +254,7 @@ defp usage_rules do | |
| 257 254 | end |
| 258 255 | ``` |
| 259 256 | |
| 260 | - This generates `.claude/skills/use-ash/SKILL.md` and `.claude/skills/use-req/SKILL.md`, each containing the package's usage rules, available mix tasks, doc search commands, and sub-rule references. |
| 261 | - |
| 262 | - You can mark deps as `:reference` to have their main rules written as a separate reference file instead of being inlined into the skill body: |
| 263 | - |
| 264 | - ```elixir |
| 265 | - skills: [ |
| 266 | - deps: [{:ash, :reference}, {~r/^ash_/, :reference}] |
| 267 | - ] |
| 268 | - ``` |
| 257 | + This generates `.claude/skills/use-ash/SKILL.md` and `.claude/skills/use-req/SKILL.md`, each with reference links to the package's usage rules, available mix tasks, doc search commands, and sub-rule references. Each package's `usage-rules.md` is written to a `references/<package>.md` file. |
| 269 258 | |
| 270 259 | ### Compose custom skills |
| 271 260 | |
| @@ -282,7 +271,7 @@ skills: [ | |
| 282 271 | ] |
| 283 272 | ``` |
| 284 273 | |
| 285 | - This generates a single `.claude/skills/ash-framework/SKILL.md` that combines usage rules from all listed packages. Regex is also supported: |
| 274 | + This generates a single `.claude/skills/ash-framework/SKILL.md` with reference links to usage rules from all listed packages. Each package's rules are written to `references/<package>.md`. Regex is also supported: |
| 286 275 | |
| 287 276 | ```elixir |
| 288 277 | skills: [ |
| @@ -295,24 +284,6 @@ skills: [ | |
| 295 284 | ] |
| 296 285 | ``` |
| 297 286 | |
| 298 | - ### Reference mode |
| 299 | - |
| 300 | - By default, each package's main `usage-rules.md` is inlined directly into the skill body. For skills that combine many packages, this can make the SKILL.md quite large. You can use `{:dep, :reference}` or `{~r/.../, :reference}` to have packages built as separate reference files instead: |
| 301 | - |
| 302 | - ```elixir |
| 303 | - skills: [ |
| 304 | - build: [ |
| 305 | - "ash-framework": [ |
| 306 | - description: "Expert on the Ash Framework ecosystem.", |
| 307 | - # Ash core rules are inlined, extensions are reference files |
| 308 | - usage_rules: [:ash, {~r/^ash_/, :reference}] |
| 309 | - ] |
| 310 | - ] |
| 311 | - ] |
| 312 | - ``` |
| 313 | - |
| 314 | - This inlines `:ash`'s rules into the skill body and writes each matching extension's rules to `references/<package>.md`, linked from the "Additional References" section — the same way sub-rules are handled. This works in both `deps` and `build` configs, and with both atoms and regexes. |
| 315 | - |
| 316 287 | ### Stale skill cleanup |
| 317 288 | |
| 318 289 | Skills generated by UsageRules include a `managed-by: usage-rules` marker in their YAML frontmatter. When a skill is removed from your config and you re-run `mix usage_rules.sync`, the stale skill files are automatically cleaned up. If you've added custom content to a managed skill, only the managed section is removed — your custom content is preserved. |
| @@ -7,7 +7,7 @@ | |
| 7 7 | {<<"GitHub">>,<<"https://github.com/ash-project/usage_rules">>}, |
| 8 8 | {<<"Website">>,<<"https://ash-hq.org">>}]}. |
| 9 9 | {<<"name">>,<<"usage_rules">>}. |
| 10 | - {<<"version">>,<<"1.0.0-rc.2">>}. |
| 10 | + {<<"version">>,<<"1.0.0-rc.3">>}. |
| 11 11 | {<<"description">>, |
| 12 12 | <<"A config-driven dev tool for Elixir projects to manage AGENTS.md files and agent skills from dependencies">>}. |
| 13 13 | {<<"elixir">>,<<"~> 1.18">>}. |
| @@ -255,9 +255,8 @@ if Code.ensure_loaded?(Igniter) do | |
| 255 255 | |> Enum.filter(fn {_name, path, _mode} -> |
| 256 256 | Igniter.exists?(igniter, Path.join(path, "usage-rules.md")) |
| 257 257 | end) |
| 258 | - |> Enum.map(fn {pkg_name, _path, mode} -> |
| 259 | - spec = if mode == :reference, do: {pkg_name, :reference}, else: pkg_name |
| 260 | - {:"use-#{pkg_name}", [usage_rules: [spec]]} |
| 258 | + |> Enum.map(fn {pkg_name, _path, _mode} -> |
| 259 | + {:"use-#{pkg_name}", [usage_rules: [pkg_name]]} |
| 261 260 | end) |
| 262 261 | |
| 263 262 | # Merge explicit build specs on top of package-derived ones |
| @@ -719,7 +718,8 @@ if Code.ensure_loaded?(Igniter) do | |
| 719 718 | resolved_packages, |
| 720 719 | custom_description |
| 721 720 | ) do |
| 722 | - skill_md = build_skill_md(igniter, skill_name, resolved_packages, custom_description) |
| 721 | + skill_md = |
| 722 | + build_skill_md(igniter, skill_name, resolved_packages, custom_description) |
| 723 723 | |
| 724 724 | igniter = |
| 725 725 | Igniter.create_or_update_file( |
| @@ -733,27 +733,23 @@ if Code.ensure_loaded?(Igniter) do | |
| 733 733 | end |
| 734 734 | ) |
| 735 735 | |
| 736 | - # Reference files for sub-rules and reference-mode main rules from all packages |
| 737 | - Enum.reduce(resolved_packages, igniter, fn {pkg_name, package_path, mode}, acc -> |
| 738 | - # Create reference file for main usage-rules.md if this package is in reference mode |
| 736 | + # Reference files for sub-rules and main rules from all packages |
| 737 | + Enum.reduce(resolved_packages, igniter, fn {pkg_name, package_path, _mode}, acc -> |
| 738 | + # Create reference file for main usage-rules.md |
| 739 739 | acc = |
| 740 | - if mode == :reference do |
| 741 | - main_path = Path.join(package_path, "usage-rules.md") |
| 742 | - content = read_dep_content(acc, main_path) |
| 743 | - ref_path = Path.join([skill_dir, "references", "#{pkg_name}.md"]) |
| 740 | + case read_dep_content(acc, Path.join(package_path, "usage-rules.md")) do |
| 741 | + "" -> |
| 742 | + acc |
| 743 | + |
| 744 | + content -> |
| 745 | + ref_path = Path.join([skill_dir, "references", "#{pkg_name}.md"]) |
| 744 746 | |
| 745 | - if content != "" do |
| 746 747 | Igniter.create_or_update_file( |
| 747 748 | acc, |
| 748 749 | ref_path, |
| 749 750 | content, |
| 750 751 | fn source -> Rewrite.Source.update(source, :content, content) end |
| 751 752 | ) |
| 752 | - else |
| 753 | - acc |
| 754 | - end |
| 755 | - else |
| 756 | - acc |
| 757 753 | end |
| 758 754 | |
| 759 755 | sub_rules = find_available_sub_rules(acc, package_path) |
| @@ -815,23 +811,21 @@ if Code.ensure_loaded?(Igniter) do | |
| 815 811 | defp build_skill_body(igniter, _skill_name, resolved_packages) do |
| 816 812 | sections = [] |
| 817 813 | |
| 818 | - # Sub-rules as references (at the top for discoverability) |
| 814 | + # Sub-rules as references |
| 819 815 | all_sub_rules = |
| 820 816 | Enum.flat_map(resolved_packages, fn {_pkg_name, package_path, _mode} -> |
| 821 817 | find_available_sub_rules(igniter, package_path) |
| 822 818 | end) |
| 823 819 | |
| 824 | - # Main rules from reference-mode packages |
| 825 | - reference_main_rules = |
| 826 | - resolved_packages |
| 827 | - |> Enum.filter(fn {_pkg_name, _path, mode} -> mode == :reference end) |
| 828 | - |> Enum.map(fn {pkg_name, _path, _mode} -> pkg_name end) |
| 820 | + # All packages are references |
| 821 | + all_main_rules = |
| 822 | + Enum.map(resolved_packages, fn {pkg_name, _path, _mode} -> pkg_name end) |
| 829 823 | |
| 830 824 | all_references = |
| 831 825 | Enum.map(all_sub_rules, fn sub_rule -> |
| 832 826 | "- [#{sub_rule}](references/#{sub_rule}.md)" |
| 833 827 | end) ++ |
| 834 | - Enum.map(reference_main_rules, fn pkg_name -> |
| 828 | + Enum.map(all_main_rules, fn pkg_name -> |
| 835 829 | "- [#{pkg_name}](references/#{pkg_name}.md)" |
| 836 830 | end) |
| 837 831 | |
| @@ -843,23 +837,6 @@ if Code.ensure_loaded?(Igniter) do | |
| 843 837 | sections |
| 844 838 | end |
| 845 839 | |
| 846 | - # Usage rules content from inline packages only |
| 847 | - sections = |
| 848 | - Enum.reduce(resolved_packages, sections, fn {pkg_name, package_path, mode}, acc -> |
| 849 | - if mode == :reference do |
| 850 | - acc |
| 851 | - else |
| 852 | - main_path = Path.join(package_path, "usage-rules.md") |
| 853 | - content = read_dep_content(igniter, main_path) |
| 854 | - |
| 855 | - if content != "" do |
| 856 | - acc ++ ["## #{pkg_name} usage rules\n\n#{content}"] |
| 857 | - else |
| 858 | - acc |
| 859 | - end |
| 860 | - end |
| 861 | - end) |
| 862 | - |
| 863 840 | # Search docs for all packages |
| 864 841 | package_names = Enum.map(resolved_packages, &elem(&1, 0)) |
| 865 842 | |
| @@ -943,12 +920,24 @@ if Code.ensure_loaded?(Igniter) do | |
| 943 920 | defp expand_dep_specs(specs, all_deps) do |
| 944 921 | Enum.flat_map(specs, fn |
| 945 922 | {%Regex{} = regex, :reference} -> |
| 923 | + IO.warn( |
| 924 | + "{~r/.../, :reference} is deprecated in usage_rules skill config. " <> |
| 925 | + "All packages are now automatically written as reference files. " <> |
| 926 | + "Use the regex directly instead: ~r/#{Regex.source(regex)}/" |
| 927 | + ) |
| 928 | + |
| 946 929 | Enum.filter(all_deps, fn {name, _path} -> |
| 947 930 | Regex.match?(regex, to_string(name)) |
| 948 931 | end) |
| 949 932 | |> Enum.map(fn {name, path} -> {name, path, :reference} end) |
| 950 933 | |
| 951 934 | {pkg_name, :reference} when is_atom(pkg_name) -> |
| 935 | + IO.warn( |
| 936 | + "{:#{pkg_name}, :reference} is deprecated in usage_rules skill config. " <> |
| 937 | + "All packages are now automatically written as reference files. " <> |
| 938 | + "Use the atom directly instead: :#{pkg_name}" |
| 939 | + ) |
| 940 | + |
| 952 941 | case Enum.find(all_deps, fn {name, _path} -> name == pkg_name end) do |
| 953 942 | nil -> [] |
| 954 943 | {name, path} -> [{name, path, :reference}] |
| @@ -958,12 +947,12 @@ if Code.ensure_loaded?(Igniter) do | |
| 958 947 | Enum.filter(all_deps, fn {name, _path} -> |
| 959 948 | Regex.match?(regex, to_string(name)) |
| 960 949 | end) |
| 961 | - |> Enum.map(fn {name, path} -> {name, path, :inline} end) |
| 950 | + |> Enum.map(fn {name, path} -> {name, path, :reference} end) |
| 962 951 | |
| 963 952 | pkg_name when is_atom(pkg_name) -> |
| 964 953 | case Enum.find(all_deps, fn {name, _path} -> name == pkg_name end) do |
| 965 954 | nil -> [] |
| 966 | - {name, path} -> [{name, path, :inline}] |
| 955 | + {name, path} -> [{name, path, :reference}] |
| 967 956 | end |
| 968 957 | end) |
| 969 958 | |> Enum.uniq_by(&elem(&1, 0)) |
| @@ -5,7 +5,7 @@ | |
| 5 5 | defmodule UsageRules.MixProject do |
| 6 6 | use Mix.Project |
| 7 7 | |
| 8 | - @version "1.0.0-rc.2" |
| 8 | + @version "1.0.0-rc.3" |
| 9 9 | @description """ |
| 10 10 | A config-driven dev tool for Elixir projects to manage AGENTS.md files and agent skills from dependencies |
| 11 11 | """ |