UsageRules

repository·main·Indexed 41 days ago

https://github.com/ash-project/usage_rules

A configuration-driven developer tool for Elixir projects designed to manage AI agent context. It consolidates usage rules from dependencies into files like AGENTS.md or CLAUDE.md, automatically generates agent-readable SKILL.md files, and provides Mix tasks for searching hexdocs and inspecting module documentation.

Tokens
7.7K
Snippets
24
Records
41
Agent score
30%

What's inside UsageRules

  1. How to use Usage Rules

    main
    Usage Rules are guidelines and rules provided directly by package authors. You should consult them thoroughly before taking action in a project, as they are the authoritative source for making decisions regarding specific packages and their intended usage patterns.
  2. Handle errors with tuples and the `with` statement

    main

    For operations that can fail, return standard result tuples: {:ok, result} for success and {:error, reason} for failure. Avoid using exceptions for normal control flow.

    To chain multiple operations that return these result tuples, use the with statement to avoid deeply nested case blocks.

  3. How to inline main rules and link sub-rules

    main

    If a package has many sub-rules and you want to keep the main rules inlined but link to sub-rules instead of inlining them all, use main: false to declare the package twice with different options.

    usage_rules: [
      {:ash, sub_rules: []},                                  # inline main rules only
      {:ash, sub_rules: :all, main: false, link: :markdown}   # link sub-rules only
    ]
  4. Use pattern matching for control flow

    main

    In Elixir, prefer pattern matching over conditional logic (if/else or case) whenever possible. A common idiomatic pattern is to use multiple function clauses (matching on function heads) to handle different data shapes instead of complex logic inside a single function body.

    Important Note on Maps: The pattern %{} matches any map, not just an empty one. To check if a map is truly empty, use a guard with map_size(map) == 0.

  5. Select appropriate data structures

    main

    Choose data structures based on the use case:

    • Structs: Use defstruct when the data shape is known and fixed.
    • Maps: Use maps for dynamic key-value data.
    • Keyword Lists: Use keyword lists (e.g., [timeout: 5000, retries: 3]) for passing options to functions.
    • Lists: When adding elements to a list, always prepend using the cons operator [new | list] instead of appending with list ++ [new] for better performance.
  6. How agent skills work

    main

    Skills are SKILL.md files that agent tools like Claude Code can discover and use. UsageRules generates these files to provide agents with context about your dependencies.

    • Auto-build (deps): Creates a use-<package> skill for each dependency. It includes reference links to usage rules, mix tasks, and doc search commands.
    • Composition (build): Allows you to group multiple packages into a single specialized skill (e.g., an "Ash Framework" skill that combines :ash, :ash_postgres, etc.).
    • Discovery: AI agents use the description: field in the skill to decide when to auto-load it. Providing specific, observable signals (like file paths or error types) in the description improves agent accuracy.
  7. Choose between GenServer.call/3 and GenServer.cast/2

    main

    When communicating with a GenServer, choose the method based on whether you require a response:

    • Synchronous Requests: Use GenServer.call/3 when you expect a reply. This provides back-pressure and ensures the caller waits for the result.
    • Fire-and-Forget: Use GenServer.cast/2 for messages where you do not need a response.

    Recommendation: When in doubt, use call over cast to maintain back-pressure. Always set appropriate timeouts for call/3 operations to prevent callers from hanging indefinitely.

  8. Best practices for implementing GenServers

    main

    When implementing GenServer processes, follow these guidelines to ensure stability and maintainability:

    • State Management: Keep the process state simple and ensure it is serializable.
    • Message Handling: Explicitly handle all expected messages in your callback functions.
    • Initialization: Use handle_continue/2 to perform heavy or asynchronous work after the initial init/1 call has completed.
    • Cleanup: Implement the terminate/2 callback to perform necessary cleanup when the process exits.
  9. Ship pre-built skills in your package

    main

    Packages can provide pre-built skills that users can pull in directly. Skills allow for more structured, modular guidance.

    Skill Directory Structure

    Place skill files in a usage-rules/skills/ directory. Each skill should have its own subdirectory containing a SKILL.md file and optional reference files.

    usage-rules/skills/
      my-skill/
        SKILL.md             # the skill definition
        references/
          some-ref.md        # optional reference files

    Defining a Skill

    Each SKILL.md must include YAML frontmatter defining the name and description.

    ---
    name: my-skill
    description: "Use this skill when working with MyPackage."
    ---
    
    ## Overview
    
    Content describing how to use the package effectively...

    Enabling Skills

    Users enable skills by listing the package in their mix.exs under the skills key:

    # In mix.exs
    package: [ 
      skills: [package_skills: [:my_package]] 
    ]

    When synced, skills are copied to the user's location with a managed-by: usage-rules marker. Companion files in references/ are copied verbatim.

  10. Use Task and Task.Supervisor for concurrent work

    main

    For managing asynchronous tasks, follow these patterns:

    • Fault Tolerance: Use Task.Supervisor instead of raw tasks to improve fault tolerance and supervision.
    • Failure Handling: Manage task failures using Task.yield/2 or Task.shutdown/2.
    • Timeouts: Always set appropriate timeouts for tasks.
    • Concurrent Enumeration: Use Task.async_stream/3 when you need to process an enumerable concurrently while maintaining back-pressure.
  11. Install UsageRules

    main

    You can install usage_rules using igniter if it is already present in your project, or by adding it manually to your mix.exs dependencies. It is recommended to include it only for the :dev environment.

    # Using igniter
    mix igniter.install usage_rules
    # Manual installation in mix.exs
    defp deps do
      [
        {:usage_rules, "~> 1.1", only: [:dev]},
        {:igniter, "~> 0.6", only: [:dev]}
      ]
    end
  12. Configure UsageRules in mix.exs

    main

    UsageRules is configured via a :usage_rules key in your mix.exs project configuration. The configuration allows you to specify a target file (like CLAUDE.md or AGENTS.md) for consolidated usage rules and define how agent skills are generated. After configuring, run mix usage_rules.sync to apply changes.

    def project do
      [
        # ...
        usage_rules: usage_rules()
      ]
    end
    
    defp usage_rules do
      [
        file: "CLAUDE.md",
        usage_rules: [:ash, ~r/^ash_/],
        skills: [
          location: ".claude/skills",
          deps: [:ash],
          build: [
            "ash-framework": [
              description: "Expert on Ash",
              usage_rules: [:ash, ~r/^ash_/]
            ]
          ]
        ]
      ]
    end