Install and use Usage Rules
requires a further cleanup of CLAUDE.md
This commit is contained in:
@@ -245,3 +245,150 @@ Security
|
|||||||
- Protect against common web vulnerabilities (XSS, CSRF, SQL injection).
|
- Protect against common web vulnerabilities (XSS, CSRF, SQL injection).
|
||||||
|
|
||||||
Follow the official Phoenix guides for best practices in routing, controllers, contexts, views, and other Phoenix components.
|
Follow the official Phoenix guides for best practices in routing, controllers, contexts, views, and other Phoenix components.
|
||||||
|
|
||||||
|
<!-- usage-rules-start -->
|
||||||
|
<!-- usage-rules-header -->
|
||||||
|
# Usage Rules
|
||||||
|
|
||||||
|
**IMPORTANT**: Consult these usage rules early and often when working with the packages listed below.
|
||||||
|
Before attempting to use any of these packages or to discover if you should use them, review their
|
||||||
|
usage rules to understand the correct patterns, conventions, and best practices.
|
||||||
|
<!-- usage-rules-header-end -->
|
||||||
|
|
||||||
|
<!-- igniter-start -->
|
||||||
|
## igniter usage
|
||||||
|
_A code generation and project patching framework
|
||||||
|
_
|
||||||
|
|
||||||
|
[igniter usage rules](deps/igniter/usage-rules.md)
|
||||||
|
<!-- igniter-end -->
|
||||||
|
<!-- usage_rules-start -->
|
||||||
|
## usage_rules usage
|
||||||
|
_A dev tool for Elixir projects to gather LLM usage rules from dependencies
|
||||||
|
_
|
||||||
|
|
||||||
|
## Using Usage Rules
|
||||||
|
|
||||||
|
Many packages have usage rules, which you should *thoroughly* consult before taking any
|
||||||
|
action. These usage rules contain guidelines and rules *directly from the package authors*.
|
||||||
|
They are your best source of knowledge for making decisions.
|
||||||
|
|
||||||
|
## Modules & functions in the current app and dependencies
|
||||||
|
|
||||||
|
When looking for docs for modules & functions that are dependencies of the current project,
|
||||||
|
or for Elixir itself, use `mix usage_rules.docs`
|
||||||
|
|
||||||
|
```
|
||||||
|
# Search a whole module
|
||||||
|
mix usage_rules.docs Enum
|
||||||
|
|
||||||
|
# Search a specific function
|
||||||
|
mix usage_rules.docs Enum.zip
|
||||||
|
|
||||||
|
# Search a specific function & arity
|
||||||
|
mix usage_rules.docs Enum.zip/1
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Searching Documentation
|
||||||
|
|
||||||
|
You should also consult the documentation of any tools you are using, early and often. The best
|
||||||
|
way to accomplish this is to use the `usage_rules.search_docs` mix task. Once you have
|
||||||
|
found what you are looking for, use the links in the search results to get more detail. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Search docs for all packages in the current application, including Elixir
|
||||||
|
mix usage_rules.search_docs Enum.zip
|
||||||
|
|
||||||
|
# Search docs for specific packages
|
||||||
|
mix usage_rules.search_docs Req.get -p req
|
||||||
|
|
||||||
|
# Search docs for multi-word queries
|
||||||
|
mix usage_rules.search_docs "making requests" -p req
|
||||||
|
|
||||||
|
# Search only in titles (useful for finding specific functions/modules)
|
||||||
|
mix usage_rules.search_docs "Enum.zip" --query-by title
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
<!-- usage_rules-end -->
|
||||||
|
<!-- usage_rules:elixir-start -->
|
||||||
|
## usage_rules:elixir usage
|
||||||
|
# Elixir Core Usage Rules
|
||||||
|
|
||||||
|
## Pattern Matching
|
||||||
|
- Use pattern matching over conditional logic when possible
|
||||||
|
- Prefer to match on function heads instead of using `if`/`else` or `case` in function bodies
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
- Use `{:ok, result}` and `{:error, reason}` tuples for operations that can fail
|
||||||
|
- Avoid raising exceptions for control flow
|
||||||
|
- Use `with` for chaining operations that return `{:ok, _}` or `{:error, _}`
|
||||||
|
|
||||||
|
## Common Mistakes to Avoid
|
||||||
|
- Elixir has no `return` statement, nor early returns. The last expression in a block is always returned.
|
||||||
|
- Don't use `Enum` functions on large collections when `Stream` is more appropriate
|
||||||
|
- Avoid nested `case` statements - refactor to a single `case`, `with` or separate functions
|
||||||
|
- Don't use `String.to_atom/1` on user input (memory leak risk)
|
||||||
|
- Lists and enumerables cannot be indexed with brackets. Use pattern matching or `Enum` functions
|
||||||
|
- Prefer `Enum` functions like `Enum.reduce` over recursion
|
||||||
|
- When recursion is necessary, prefer to use pattern matching in function heads for base case detection
|
||||||
|
- Using the process dictionary is typically a sign of unidiomatic code
|
||||||
|
- Only use macros if explicitly requested
|
||||||
|
- There are many useful standard library functions, prefer to use them where possible
|
||||||
|
|
||||||
|
## Function Design
|
||||||
|
- Use guard clauses: `when is_binary(name) and byte_size(name) > 0`
|
||||||
|
- Prefer multiple function clauses over complex conditional logic
|
||||||
|
- Name functions descriptively: `calculate_total_price/2` not `calc/2`
|
||||||
|
- Predicate function names should not start with `is` and should end in a question mark.
|
||||||
|
- Names like `is_thing` should be reserved for guards
|
||||||
|
|
||||||
|
## Data Structures
|
||||||
|
- Use structs over maps when the shape is known: `defstruct [:name, :age]`
|
||||||
|
- Prefer keyword lists for options: `[timeout: 5000, retries: 3]`
|
||||||
|
- Use maps for dynamic key-value data
|
||||||
|
- Prefer to prepend to lists `[new | list]` not `list ++ [new]`
|
||||||
|
|
||||||
|
## Mix Tasks
|
||||||
|
|
||||||
|
- Use `mix help` to list available mix tasks
|
||||||
|
- Use `mix help task_name` to get docs for an individual task
|
||||||
|
- Read the docs and options fully before using tasks
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
- Run tests in a specific file with `mix test test/my_test.exs` and a specific test
|
||||||
|
with the line number `mix test path/to/test.exs:123`
|
||||||
|
- Limit the number of failed tests with `mix test --max-failures n`
|
||||||
|
- Use `@tag` to tag specific tests, and `mix test --only tag` to run only those tests
|
||||||
|
- Use `assert_raise` for testing expected exceptions: `assert_raise ArgumentError, fn -> invalid_function() end`
|
||||||
|
|
||||||
|
<!-- usage_rules:elixir-end -->
|
||||||
|
<!-- usage_rules:otp-start -->
|
||||||
|
## usage_rules:otp usage
|
||||||
|
# OTP Usage Rules
|
||||||
|
|
||||||
|
## GenServer Best Practices
|
||||||
|
- Keep state simple and serializable
|
||||||
|
- Handle all expected messages explicitly
|
||||||
|
- Use `handle_continue/2` for post-init work
|
||||||
|
- Implement proper cleanup in `terminate/2` when necessary
|
||||||
|
|
||||||
|
## Process Communication
|
||||||
|
- Use `GenServer.call/3` for synchronous requests expecting replies
|
||||||
|
- Use `GenServer.cast/2` for fire-and-forget messages.
|
||||||
|
- When in doubt, us `call` over `cast`, to ensure back-pressure
|
||||||
|
- Set appropriate timeouts for `call/3` operations
|
||||||
|
|
||||||
|
## Fault Tolerance
|
||||||
|
- Set up processes such that they can handle crashing and being restarted by supervisors
|
||||||
|
- Use `:max_restarts` and `:max_seconds` to prevent restart loops
|
||||||
|
|
||||||
|
## Task and Async
|
||||||
|
- Use `Task.Supervisor` for better fault tolerance
|
||||||
|
- Handle task failures with `Task.yield/2` or `Task.shutdown/2`
|
||||||
|
- Set appropriate task timeouts
|
||||||
|
- Use `Task.async_stream/3` for concurrent enumeration with back-pressure
|
||||||
|
|
||||||
|
<!-- usage_rules:otp-end -->
|
||||||
|
<!-- usage-rules-end -->
|
||||||
|
|||||||
@@ -118,6 +118,16 @@ depends = [
|
|||||||
description = 'Install dependencies and setup the database'
|
description = 'Install dependencies and setup the database'
|
||||||
run = ['mix setup']
|
run = ['mix setup']
|
||||||
|
|
||||||
|
# AI
|
||||||
|
|
||||||
|
[tasks."usage-rules:sync"]
|
||||||
|
run = """
|
||||||
|
mix usage_rules.sync CLAUDE.md --all \
|
||||||
|
--inline usage_rules:all \
|
||||||
|
--link-to-folder deps \
|
||||||
|
--remove-missing
|
||||||
|
"""
|
||||||
|
|
||||||
# PRODUCTION
|
# PRODUCTION
|
||||||
|
|
||||||
[tasks."prod:console"]
|
[tasks."prod:console"]
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ defmodule MusicLibrary.MixProject do
|
|||||||
|
|
||||||
# Dev tooling
|
# Dev tooling
|
||||||
{:igniter, "~> 0.6", only: [:dev, :test]},
|
{:igniter, "~> 0.6", only: [:dev, :test]},
|
||||||
|
{:usage_rules, "~> 0.1", only: [:dev]},
|
||||||
{:phoenix_live_reload, "~> 1.2", only: :dev},
|
{:phoenix_live_reload, "~> 1.2", only: :dev},
|
||||||
{:esbuild, "~> 0.8", runtime: Mix.env() == :dev},
|
{:esbuild, "~> 0.8", runtime: Mix.env() == :dev},
|
||||||
{:tailwind, "~> 0.3", runtime: Mix.env() == :dev},
|
{:tailwind, "~> 0.3", runtime: Mix.env() == :dev},
|
||||||
|
|||||||
@@ -69,6 +69,7 @@
|
|||||||
"thousand_island": {:hex, :thousand_island, "1.3.14", "ad45ebed2577b5437582bcc79c5eccd1e2a8c326abf6a3464ab6c06e2055a34a", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d0d24a929d31cdd1d7903a4fe7f2409afeedff092d277be604966cd6aa4307ef"},
|
"thousand_island": {:hex, :thousand_island, "1.3.14", "ad45ebed2577b5437582bcc79c5eccd1e2a8c326abf6a3464ab6c06e2055a34a", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d0d24a929d31cdd1d7903a4fe7f2409afeedff092d277be604966cd6aa4307ef"},
|
||||||
"tidewave": {:hex, :tidewave, "0.2.0", "e98378803e535d3035138e4b354dcfca26b7f862fd44cffef5aa697b814c0b0b", [:mix], [{:circular_buffer, "~> 0.4 or ~> 1.0", [hex: :circular_buffer, repo: "hexpm", optional: false]}, {:igniter, ">= 0.5.47 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:plug, "~> 1.17", [hex: :plug, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "6ad11829f4600cd69955ffc66935e6456b775fea095172147244ba6f65986735"},
|
"tidewave": {:hex, :tidewave, "0.2.0", "e98378803e535d3035138e4b354dcfca26b7f862fd44cffef5aa697b814c0b0b", [:mix], [{:circular_buffer, "~> 0.4 or ~> 1.0", [hex: :circular_buffer, repo: "hexpm", optional: false]}, {:igniter, ">= 0.5.47 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:plug, "~> 1.17", [hex: :plug, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "6ad11829f4600cd69955ffc66935e6456b775fea095172147244ba6f65986735"},
|
||||||
"time_zone_info": {:hex, :time_zone_info, "0.7.8", "5b0e2b6b4af7b7526ef519dfd919d9d89d493a7b5e12a07434f2760dd5fe8be0", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "307d3fab87679e80888ce39fdfcf44a8fd6189cefc2fca3a83fb5c1097efa40e"},
|
"time_zone_info": {:hex, :time_zone_info, "0.7.8", "5b0e2b6b4af7b7526ef519dfd919d9d89d493a7b5e12a07434f2760dd5fe8be0", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "307d3fab87679e80888ce39fdfcf44a8fd6189cefc2fca3a83fb5c1097efa40e"},
|
||||||
|
"usage_rules": {:hex, :usage_rules, "0.1.21", "5869383bd23ffc1ee499eb00614106b36fb1f554a17706e27cb84725e72d925a", [:mix], [{:igniter, ">= 0.6.6 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "5d2eadd4e7506c0ef995ca05362ac54ace7e6d2311d391cbb678a85f3095eeb0"},
|
||||||
"vix": {:hex, :vix, "0.35.0", "f6319b715e3b072e53eba456a21af5f2ff010a7a7b19b884600ea98a0609b18c", [:make, :mix], [{:cc_precompiler, "~> 0.1.4 or ~> 0.2", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.7.3 or ~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: true]}], "hexpm", "a3e80067a89d0631b6cf2b93594e03c1b303a2c7cddbbdd28040750d521984e5"},
|
"vix": {:hex, :vix, "0.35.0", "f6319b715e3b072e53eba456a21af5f2ff010a7a7b19b884600ea98a0609b18c", [:make, :mix], [{:cc_precompiler, "~> 0.1.4 or ~> 0.2", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.7.3 or ~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: true]}], "hexpm", "a3e80067a89d0631b6cf2b93594e03c1b303a2c7cddbbdd28040750d521984e5"},
|
||||||
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
|
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
|
||||||
"websock_adapter": {:hex, :websock_adapter, "0.5.8", "3b97dc94e407e2d1fc666b2fb9acf6be81a1798a2602294aac000260a7c4a47d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "315b9a1865552212b5f35140ad194e67ce31af45bcee443d4ecb96b5fd3f3782"},
|
"websock_adapter": {:hex, :websock_adapter, "0.5.8", "3b97dc94e407e2d1fc666b2fb9acf6be81a1798a2602294aac000260a7c4a47d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "315b9a1865552212b5f35140ad194e67ce31af45bcee443d4ecb96b5fd3f3782"},
|
||||||
|
|||||||
Reference in New Issue
Block a user