Current section

42 Versions

Jump to

Compare versions

5 files changed
+173 additions
-54 deletions
  @@ -5,6 +5,17 @@ See [Conventional Commits](Https://conventionalcommits.org) for commit guideline
5 5
6 6 <!-- changelog -->
7 7
8 + ## v0.1.11 (2025-06-25)
9 +
10 +
11 +
12 +
13 + ### Improvements:
14 +
15 + * add descriptions to usage rules by Zach Daniel
16 +
17 + * make builtin usage rules always show inline by Zach Daniel
18 +
8 19 ## v0.1.10 (2025-06-25)
  @@ -5,16 +5,12 @@
5 5 ## Quickstart
6 6
7 7 ```sh
8 - # swap AGENTS.md out for a different file
9 - # if you need, like `CLAUDE.md`
10 -
11 - # sync core usage rules directly into your
12 - # agents file
13 - mix usage_rules.sync AGENTS.md --builtins elixir,otp
8 + # swap AGENTS.md out for any file you like, e.g `CLAUDE.md`
14 9
15 10 # sync projects as links to their usage rules
16 11 # to save tokens. Agent can view them on demand
17 12 mix usage_rules.sync AGENTS.md --all \
13 + --builtins elixir,otp \
18 14 --link-to-folder deps
19 15 ```
  @@ -6,7 +6,7 @@
6 6 {<<"GitHub">>,<<"https://github.com/ash-project/usage_rules">>},
7 7 {<<"Website">>,<<"https://ash-hq.org">>}]}.
8 8 {<<"name">>,<<"usage_rules">>}.
9 - {<<"version">>,<<"0.1.10">>}.
9 + {<<"version">>,<<"0.1.11">>}.
10 10 {<<"description">>,
11 11 <<"A dev tool for Elixir projects to gather LLM usage rules from dependencies">>}.
12 12 {<<"elixir">>,<<"~> 1.18">>}.
  @@ -24,6 +24,7 @@ defmodule Mix.Tasks.UsageRules.Sync.Docs do
24 24 * `--link-to-folder <folder>` - Save usage rules for each package in separate files within the specified folder and create links to them
25 25 * `--link-style <style>` - Style of links to create when using --link-to-folder (markdown|at). Defaults to 'markdown'
26 26 * `--builtins <builtins>` - Include built-in usage rules (comma-separated: elixir,otp)
27 + * `--builtins-link` - Make built-in usage rules honor the --link-to-folder option (default: false, builtins are inlined)
27 28
28 29 ## Examples
29 30
  @@ -86,6 +87,10 @@ defmodule Mix.Tasks.UsageRules.Sync.Docs do
86 87 ```sh
87 88 mix usage_rules.sync CLAUDE.md --all --link-to-folder deps --builtins elixir,otp
88 89 ```
90 + Include built-in usage rules with links to folder:
91 + ```sh
92 + mix usage_rules.sync CLAUDE.md --all --link-to-folder docs --builtins elixir,otp --builtins-link
93 + ```
89 94 """
90 95 end
91 96 end
  @@ -115,7 +120,8 @@ if Code.ensure_loaded?(Igniter) do
115 120 remove: :boolean,
116 121 link_to_folder: :string,
117 122 link_style: :string,
118 - builtins: :string
123 + builtins: :string,
124 + builtins_link: :boolean
119 125 ]
120 126 }
121 127 end
  @@ -157,6 +163,7 @@ if Code.ensure_loaded?(Igniter) do
157 163 link_style = igniter.args.options[:link_style] || "markdown"
158 164 provided_packages = igniter.args.positional.packages
159 165 builtins = parse_builtins(igniter.args.options[:builtins])
166 + builtins_link = igniter.args.options[:builtins_link]
160 167
161 168 cond do
162 169 # If --builtins contains invalid values, add error
  @@ -211,7 +218,14 @@ if Code.ensure_loaded?(Igniter) do
211 218
212 219 # Handle --all option
213 220 all_option ->
214 - handle_all_option(igniter, all_deps, link_to_folder, link_style, builtins)
221 + handle_all_option(
222 + igniter,
223 + all_deps,
224 + link_to_folder,
225 + link_style,
226 + builtins,
227 + builtins_link
228 + )
215 229
216 230 # Handle --list option
217 231 list_option ->
  @@ -225,7 +239,8 @@ if Code.ensure_loaded?(Igniter) do
225 239 provided_packages,
226 240 link_to_folder,
227 241 link_style,
228 - builtins
242 + builtins,
243 + builtins_link
229 244 )
230 245 end
231 246 end
  @@ -258,14 +273,43 @@ if Code.ensure_loaded?(Igniter) do
258 273 builtin_path = Path.join([:code.priv_dir(:usage_rules), "builtins", "#{builtin}.md"])
259 274 content = File.read!(builtin_path)
260 275
276 + description =
277 + case builtin do
278 + "elixir" -> "Core Elixir language features and standard library"
279 + "otp" -> "OTP (Open Telecom Platform) behaviors and patterns"
280 + _ -> ""
281 + end
282 +
283 + description_part = if description == "", do: "", else: "_#{description}_\n\n"
284 +
261 285 {String.to_atom(builtin),
262 286 "<!-- #{builtin}-start -->\n" <>
263 287 "## #{builtin} usage\n" <>
288 + description_part <>
264 289 content <>
265 290 "\n<!-- #{builtin}-end -->"}
266 291 end)
267 292 end
268 293
294 + defp usage_rules_header do
295 + """
296 + <!-- usage-rules-header -->
297 + # Usage Rules
298 +
299 + **IMPORTANT**: Consult these usage rules early and often when working with the packages listed below.
300 + Before attempting to use any of these packages or to discover if you should use them, review their
301 + usage rules to understand the correct patterns, conventions, and best practices.
302 + <!-- usage-rules-header-end -->
303 + """
304 + end
305 +
306 + defp get_package_description(name) do
307 + case Application.spec(name, :description) do
308 + nil -> ""
309 + desc -> to_string(desc)
310 + end
311 + end
312 +
269 313 defp get_deps_from_igniter(igniter) do
270 314 if igniter.assigns[:test_mode?] do
271 315 igniter.rewrite.sources
  @@ -320,7 +364,7 @@ if Code.ensure_loaded?(Igniter) do
320 364 """)
321 365 end
322 366
323 - defp handle_all_option(igniter, all_deps, link_to_folder, link_style, builtins) do
367 + defp handle_all_option(igniter, all_deps, link_to_folder, link_style, builtins, builtins_link) do
324 368 all_packages_with_rules = get_packages_with_usage_rules(igniter, all_deps)
325 369
326 370 igniter
  @@ -333,7 +377,13 @@ if Code.ensure_loaded?(Igniter) do
333 377 end)
334 378 end)
335 379 |> maybe_add_builtin_notices(builtins)
336 - |> generate_usage_rules_file(all_packages_with_rules, link_to_folder, link_style, builtins)
380 + |> generate_usage_rules_file(
381 + all_packages_with_rules,
382 + link_to_folder,
383 + link_style,
384 + builtins,
385 + builtins_link
386 + )
337 387 end
338 388
339 389 defp maybe_add_builtin_notices(igniter, []), do: igniter
  @@ -371,7 +421,8 @@ if Code.ensure_loaded?(Igniter) do
371 421 provided_packages,
372 422 link_to_folder,
373 423 link_style,
374 - builtins
424 + builtins,
425 + builtins_link
375 426 ) do
376 427 packages =
377 428 all_deps
  @@ -390,7 +441,7 @@ if Code.ensure_loaded?(Igniter) do
390 441
391 442 igniter
392 443 |> maybe_add_builtin_notices(builtins)
393 - |> generate_usage_rules_file(packages, link_to_folder, link_style, builtins)
444 + |> generate_usage_rules_file(packages, link_to_folder, link_style, builtins, builtins_link)
394 445 end
395 446
396 447 defp handle_remove_packages(igniter, provided_packages, link_to_folder) do
  @@ -464,14 +515,22 @@ if Code.ensure_loaded?(Igniter) do
464 515 end
465 516 end
466 517
467 - defp generate_usage_rules_file(igniter, packages, link_to_folder, link_style, builtins) do
518 + defp generate_usage_rules_file(
519 + igniter,
520 + packages,
521 + link_to_folder,
522 + link_style,
523 + builtins,
524 + builtins_link
525 + ) do
468 526 if link_to_folder do
469 527 generate_usage_rules_with_folder_links(
470 528 igniter,
471 529 packages,
472 530 link_to_folder,
473 531 link_style,
474 - builtins
532 + builtins,
533 + builtins_link
475 534 )
476 535 else
477 536 generate_usage_rules_inline(igniter, packages, builtins)
  @@ -490,9 +549,13 @@ if Code.ensure_loaded?(Igniter) do
490 549 {:error, _} -> File.read!(usage_rules_path)
491 550 end
492 551
552 + description = get_package_description(name)
553 + description_part = if description == "", do: "", else: "_#{description}_\n\n"
554 +
493 555 {name,
494 556 "<!-- #{name}-start -->\n" <>
495 557 "## #{name} usage\n" <>
558 + description_part <>
496 559 content <>
497 560 "\n<!-- #{name}-end -->"}
498 561 end)
  @@ -504,6 +567,8 @@ if Code.ensure_loaded?(Igniter) do
504 567
505 568 full_contents_for_new_file =
506 569 "<!-- usage-rules-start -->\n" <>
570 + usage_rules_header() <>
571 + "\n" <>
507 572 all_rules_content <>
508 573 "\n<!-- usage-rules-end -->"
509 574
  @@ -534,9 +599,17 @@ if Code.ensure_loaded?(Igniter) do
534 599 end
535 600 end)
536 601 |> then(fn content ->
602 + # Ensure header is present
603 + content_with_header =
604 + if String.contains?(content, "<!-- usage-rules-header -->") do
605 + content
606 + else
607 + usage_rules_header() <> "\n" <> content
608 + end
609 +
537 610 prelude <>
538 611 "<!-- usage-rules-start -->\n" <>
539 - content <>
612 + content_with_header <>
540 613 "\n<!-- usage-rules-end -->" <>
541 614 postlude
542 615 end)
  @@ -544,6 +617,8 @@ if Code.ensure_loaded?(Igniter) do
544 617 _ ->
545 618 current_contents <>
546 619 "\n<!-- usage-rules-start -->\n" <>
620 + usage_rules_header() <>
621 + "\n" <>
547 622 all_rules_content <>
548 623 "\n<!-- usage-rules-end -->\n"
549 624 end
  @@ -558,31 +633,36 @@ if Code.ensure_loaded?(Igniter) do
558 633 packages,
559 634 folder_name,
560 635 link_style,
561 - builtins
636 + builtins,
637 + builtins_link
562 638 ) do
563 639 # Create individual files for each package in the folder (unless folder is "deps")
564 640 igniter =
565 641 if folder_name == "deps" do
566 642 igniter
567 643 else
568 - # Create builtin files in the target folder
644 + # Create builtin files in the target folder only if builtins_link is true
569 645 igniter =
570 - Enum.reduce(builtins, igniter, fn builtin, acc ->
571 - builtin_source_path =
572 - Path.join([:code.priv_dir(:usage_rules), "builtins", "#{builtin}.md"])
646 + if builtins_link do
647 + Enum.reduce(builtins, igniter, fn builtin, acc ->
648 + builtin_source_path =
649 + Path.join([:code.priv_dir(:usage_rules), "builtins", "#{builtin}.md"])
573 650
574 - builtin_file_path = Path.join(folder_name, "#{builtin}.md")
575 - content = File.read!(builtin_source_path)
651 + builtin_file_path = Path.join(folder_name, "#{builtin}.md")
652 + content = File.read!(builtin_source_path)
576 653
577 - Igniter.create_or_update_file(
578 - acc,
579 - builtin_file_path,
580 - content,
581 - fn source ->
582 - Rewrite.Source.update(source, :content, content)
583 - end
584 - )
585 - end)
654 + Igniter.create_or_update_file(
655 + acc,
656 + builtin_file_path,
657 + content,
658 + fn source ->
659 + Rewrite.Source.update(source, :content, content)
660 + end
661 + )
662 + end)
663 + else
664 + igniter
665 + end
586 666
587 667 Enum.reduce(packages, igniter, fn {name, path}, acc ->
588 668 usage_rules_path = Path.join(path, "usage-rules.md")
  @@ -618,43 +698,65 @@ if Code.ensure_loaded?(Igniter) do
618 698 _ -> "[#{name} usage rules](#{folder_name}/#{name}.md)"
619 699 end
620 700
701 + description = get_package_description(name)
702 + description_part = if description == "", do: "", else: "_#{description}_\n\n"
703 +
621 704 {name,
622 705 "<!-- #{name}-start -->\n" <>
623 706 "## #{name} usage\n" <>
707 + description_part <>
624 708 link_content <>
625 709 "\n<!-- #{name}-end -->"}
626 710 end)
627 711
628 712 builtin_contents =
629 - builtins
630 - |> Enum.map(fn builtin ->
631 - link_content =
632 - case {link_style, folder_name} do
633 - {"at", "deps"} ->
634 - "@deps/usage_rules/priv/builtins/#{builtin}.md"
713 + if builtins_link do
714 + # If builtins_link is true, create links
715 + builtins
716 + |> Enum.map(fn builtin ->
717 + link_content =
718 + case {link_style, folder_name} do
719 + {"at", "deps"} ->
720 + "@deps/usage_rules/priv/builtins/#{builtin}.md"
635 721
636 - {"at", _} ->
637 - "@#{folder_name}/#{builtin}.md"
722 + {"at", _} ->
723 + "@#{folder_name}/#{builtin}.md"
638 724
639 - {_, "deps"} ->
640 - "[#{builtin} usage rules](deps/usage_rules/priv/builtins/#{builtin}.md)"
725 + {_, "deps"} ->
726 + "[#{builtin} usage rules](deps/usage_rules/priv/builtins/#{builtin}.md)"
641 727
642 - _ ->
643 - "[#{builtin} usage rules](#{folder_name}/#{builtin}.md)"
644 - end
728 + _ ->
729 + "[#{builtin} usage rules](#{folder_name}/#{builtin}.md)"
730 + end
645 731
646 - {String.to_atom(builtin),
647 - "<!-- #{builtin}-start -->\n" <>
648 - "## #{builtin} usage\n" <>
649 - link_content <>
650 - "\n<!-- #{builtin}-end -->"}
651 - end)
732 + description =
733 + case builtin do
734 + "elixir" -> "Core Elixir language features and standard library"
735 + "otp" -> "OTP (Open Telecom Platform) behaviors and patterns"
736 + _ -> ""
737 + end
738 +
739 + description_part = if description == "", do: "", else: "_#{description}_\n\n"
740 +
741 + {String.to_atom(builtin),
742 + "<!-- #{builtin}-start -->\n" <>
743 + "## #{builtin} usage\n" <>
744 + description_part <>
745 + link_content <>
746 + "\n<!-- #{builtin}-end -->"}
747 + end)
748 + else
749 + # If builtins_link is false (default), inline the content
750 + get_builtin_contents(builtins)
751 + end
652 752
653 753 all_contents = package_contents ++ builtin_contents
654 754 all_rules_content = Enum.map_join(all_contents, "\n", &elem(&1, 1))
655 755
656 756 full_contents_for_new_file =
657 757 "<!-- usage-rules-start -->\n" <>
758 + usage_rules_header() <>
759 + "\n" <>
658 760 all_rules_content <>
659 761 "\n<!-- usage-rules-end -->"
660 762
  @@ -685,9 +787,17 @@ if Code.ensure_loaded?(Igniter) do
685 787 end
686 788 end)
687 789 |> then(fn content ->
790 + # Ensure header is present
791 + content_with_header =
792 + if String.contains?(content, "<!-- usage-rules-header -->") do
793 + content
794 + else
795 + usage_rules_header() <> "\n" <> content
796 + end
797 +
688 798 prelude <>
689 799 "<!-- usage-rules-start -->\n" <>
690 - content <>
800 + content_with_header <>
691 801 "\n<!-- usage-rules-end -->" <>
692 802 postlude
693 803 end)
  @@ -695,6 +805,8 @@ if Code.ensure_loaded?(Igniter) do
695 805 _ ->
696 806 current_contents <>
697 807 "\n<!-- usage-rules-start -->\n" <>
808 + usage_rules_header() <>
809 + "\n" <>
698 810 all_rules_content <>
699 811 "\n<!-- usage-rules-end -->\n"
700 812 end
  @@ -1,7 +1,7 @@
1 1 defmodule UsageRules.MixProject do
2 2 use Mix.Project
3 3
4 - @version "0.1.10"
4 + @version "0.1.11"
5 5 @description """
6 6 A dev tool for Elixir projects to gather LLM usage rules from dependencies
7 7 """