diff --git a/AGENTS.md b/AGENTS.md index 8cb4d32b..9488a9fb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,4 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +# AGENTS.md ## Project Overview @@ -241,19 +239,24 @@ usage rules to understand the correct patterns, conventions, and best practices. + ## igniter usage + _A code generation and project patching framework_ [igniter usage rules](deps/igniter/usage-rules.md) + + ## 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*. +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 @@ -272,10 +275,9 @@ mix usage_rules.docs Enum.zip 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 +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: @@ -293,23 +295,27 @@ mix usage_rules.search_docs "making requests" -p req mix usage_rules.search_docs "Enum.zip" --query-by title ``` - + ## 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 - `%{}` matches ANY map, not just empty maps. Use `map_size(map) == 0` guard to check for truly empty maps ## 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 @@ -322,6 +328,7 @@ mix usage_rules.search_docs "Enum.zip" --query-by title - 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` @@ -329,6 +336,7 @@ mix usage_rules.search_docs "Enum.zip" --query-by title - 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 @@ -341,6 +349,7 @@ mix usage_rules.search_docs "Enum.zip" --query-by title - 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 @@ -353,26 +362,32 @@ mix usage_rules.search_docs "Enum.zip" --query-by title + ## 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, use `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 @@ -380,7 +395,9 @@ mix usage_rules.search_docs "Enum.zip" --query-by title + ## phoenix:ecto usage + ## Ecto Guidelines - **Always** preload Ecto associations in queries when they'll be accessed in templates, ie a message that needs to reference the `message.user.email` @@ -392,7 +409,9 @@ mix usage_rules.search_docs "Enum.zip" --query-by title + ## phoenix:elixir usage + ## Elixir guidelines - Elixir lists **do not support index based access via the access syntax** @@ -410,7 +429,7 @@ mix usage_rules.search_docs "Enum.zip" --query-by title Enum.at(mylist, i) - Elixir variables are immutable, but can be rebound, so for block expressions like `if`, `case`, `cond`, etc - you *must* bind the result of the expression to a variable if you want to use it and you CANNOT rebind the result inside the expression, ie: + you _must_ bind the result of the expression to a variable if you want to use it and you CANNOT rebind the result inside the expression, ie: # INVALID: we are rebinding inside the `if` and the result never gets assigned if connected?(socket) do @@ -439,7 +458,9 @@ mix usage_rules.search_docs "Enum.zip" --query-by title + ## phoenix:html usage + ## Phoenix HTML guidelines - Phoenix templates **always** use `~H` or .html.heex files (known as HEEx), **never** use `~E` @@ -448,7 +469,7 @@ mix usage_rules.search_docs "Enum.zip" --query-by title - **Always** add unique DOM IDs to key elements (like forms, buttons, etc) when writing templates, these IDs can later be used in tests (`<.form for={@form} id="product-form">`) - For "app wide" template imports, you can import/alias into the `my_app_web.ex`'s `html_helpers` block, so they will be available to all LiveViews, LiveComponent's, and all modules that do `use MyAppWeb, :html` (replace "my_app" by the actual app name) -- Elixir supports `if/else` but **does NOT support `if/else if` or `if/elsif`. **Never use `else if` or `elseif` in Elixir**, **always** use `cond` or `case` for multiple conditionals. +- Elixir supports `if/else` but **does NOT support `if/else if` or `if/elsif`. **Never use `else if` or `elseif` in Elixir**,**always\*\* use `cond` or `case` for multiple conditionals. **Never do this (invalid)**: @@ -469,7 +490,7 @@ mix usage_rules.search_docs "Enum.zip" --query-by title ... <% end %> -- HEEx require special tag annotation if you want to insert literal curly's like `{` or `}`. If you want to show a textual code snippet on the page in a `
` or `` block you *must* annotate the parent tag with `phx-no-curly-interpolation`:
+- HEEx require special tag annotation if you want to insert literal curly's like `{` or `}`. If you want to show a textual code snippet on the page in a `` or `` block you _must_ annotate the parent tag with `phx-no-curly-interpolation`:
let obj = {key: "val"}
@@ -519,10 +540,12 @@ mix usage_rules.search_docs "Enum.zip" --query-by title
+
## phoenix:liveview usage
+
## Phoenix LiveView guidelines
-- **Never** use the deprecated `live_redirect` and `live_patch` functions, instead **always** use the `<.link navigate={href}>` and `<.link patch={href}>` in templates, and `push_navigate` and `push_patch` functions LiveViews
+- **Never** use the deprecated `live_redirect` and `live_patch` functions, instead **always** use the `<.link navigate={href}>` and `<.link patch={href}>` in templates, and `push_navigate` and `push_patch` functions LiveViews
- **Avoid LiveComponent's** unless you have a strong, specific need for them
- LiveViews should be named like `AppWeb.WeatherLive`, with a `Live` suffix. When you go to add LiveView routes to the router, the default `:browser` scope is **already aliased** with the `AppWeb` module, so you can just do `live "/weather", WeatherLive`
- Remember anytime you use `phx-hook="MyHook"` and that js hook manages its own DOM, you **must** also set the `phx-update="ignore"` attribute
@@ -544,7 +567,7 @@ mix usage_rules.search_docs "Enum.zip" --query-by title
-- LiveView streams are *not* enumerable, so you cannot use `Enum.filter/2` or `Enum.reject/2` on them. Instead, if you want to filter, prune, or refresh a list of items on the UI, you **must refetch the data and re-stream the entire stream collection, passing reset: true**:
+- LiveView streams are _not_ enumerable, so you cannot use `Enum.filter/2` or `Enum.reject/2` on them. Instead, if you want to filter, prune, or refresh a list of items on the UI, you **must refetch the data and re-stream the entire stream collection, passing reset: true**:
def handle_event("filter", %{"filter" => filter}, socket) do
# re-fetch the messages based on the filter
@@ -557,7 +580,7 @@ mix usage_rules.search_docs "Enum.zip" --query-by title
|> stream(:messages, messages, reset: true)}
end
-- LiveView streams *do not support counting or empty states*. If you need to display a count, you must track it using a separate assign. For empty states, you can use Tailwind classes:
+- LiveView streams _do not support counting or empty states_. If you need to display a count, you must track it using a separate assign. For empty states, you can use Tailwind classes:
No tasks yet
@@ -651,7 +674,9 @@ And **never** do this:
+
## phoenix:phoenix usage
+
## Phoenix guidelines
- Remember Phoenix router `scope` blocks include an optional alias which is prefixed for all routes within the scope. **Always** be mindful of this when creating routes within a scope to avoid duplicate module prefixes.
@@ -670,9 +695,12 @@ And **never** do this:
+
## fluxon usage
+
_Fluxon UI Components_
[fluxon usage rules](deps/fluxon/usage-rules.md)
+