phx-document
'Use when asked to document Elixir code: add or fill in @moduledoc and
Install
npx skills add https://github.com/oliver-kriska/claude-elixir-phoenix/tree/main/targets/amp/skills/phx-document
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install oliver-kriska-claude-elixir-phoenix@llmmart
git clone https://github.com/oliver-kriska/claude-elixir-phoenix.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole oliver-kriska/claude-elixir-phoenix collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Document
Generate documentation for newly implemented features.
Usage
phx-document .claude/plans/magic-link-auth/plan.md
phx-document magic link authentication
phx-document # Auto-detect from recent plan
Iron Laws
- Never remove existing documentation — Existing docs may reflect design intent that isn't obvious from code alone; update rather than replace
- @moduledoc on every public module — Undocumented modules accumulate quickly and create onboarding friction for new team members
- ADRs capture the "why", not the "what" — Code shows what was built; ADRs explain why this approach was chosen over alternatives
- Match @doc to function's public API — Document parameters, return values, and edge cases; callers shouldn't need to read the implementation
- DO NOT add @doc to untested code — documentation implies a stable contract; document only after tests confirm the function behaves as described
What Gets Documented
| Output | Description |
|---|---|
@moduledoc |
For new modules missing documentation |
@doc |
For public functions without docs |
| README section | For user-facing features |
| ADR | For significant architectural decisions |
Workflow
Step 0: Pre-check (avoid no-op runs)
Run git diff --name-only HEAD~5 | grep '\.ex$' | head -20 to check for new .ex files.
If no new .ex files were added (only modifications), skip the full
audit and report: "No new modules — documentation coverage unchanged."
A full audit of unchanged coverage produces nothing to add.
- Identify new modules from recent commits or plan file
- Check documentation coverage (
@moduledoc,@doc) - Generate missing docs using templates
- Add README section if user-facing feature
- Create ADR if architectural decision was made
- Write report to
.claude/plans/{slug}/reviews/{feature}-docs.md
When to Generate ADRs
| Trigger | Create ADR |
|---|---|
| New external dependency | Yes |
| New database table | Maybe (if schema non-obvious) |
| New OTP process | Yes (explain why process needed) |
| New context | Maybe (if boundaries non-obvious) |
| New auth mechanism | Yes |
| Performance optimization | Yes |
Integration with Workflow
phx-plan → phx-work → phx-review
↓
phx-document ← YOU ARE HERE (optional, suggested after review passes)
References
references/doc-templates.md— @moduledoc, @doc, README, ADR templatesreferences/output-format.md— Documentation report formatreferences/doc-best-practices.md— Elixir documentation best practicesreferences/documentation-patterns.md— Detailed documentation patterns
Files (claude-elixir-phoenix)
-
references
-
doc-best-practices.md 600 B
# Elixir Documentation Best Practices ## @moduledoc - First line: One sentence summary - Include `## Usage` with iex examples - Include `## Options` if configurable - Link to related modules with `See also` ## @doc - First line: What it does (imperative) - `## Parameters` with types - `## Returns` with tagged tuples - `## Examples` with iex ## Typespecs Always pair @doc with @spec: ```elixir @doc "Creates a magic token for the given user." @spec create_magic_token(User.t()) :: {:ok, MagicToken.t()} | {:error, Ecto.Changeset.t()} def create_magic_token(%User{} = user) do # ... end ``` -
doc-templates.md 1.1 KB
# Documentation Templates ## @moduledoc Template ```elixir @moduledoc """ {Brief description of module purpose}. ## Usage iex> MyApp.Module.function(arg) :result ## Options * `:option` - Description of option ## Examples # Example usage MyApp.Module.do_thing() """ ``` ## @doc Template ```elixir @doc """ {Brief description}. ## Parameters * `param` - Description ## Returns * `{:ok, result}` - On success * `{:error, reason}` - On failure ## Examples iex> function(:arg) {:ok, :result} """ ``` ## README Section Template For features users interact with: ````markdown ## {Feature Name} {Brief description} ### Configuration ```elixir # config/config.exs config :my_app, :feature, option: value ``` ### Usage {How to use the feature} ```` ## ADR Template Create `docs/adr/{number}-{title}.md`: ```markdown # ADR-{n}: {Title} **Date**: {date} **Status**: Accepted **Context**: {Why this decision was needed} ## Decision {What was decided} ## Consequences ### Positive - {benefit} ### Negative - {tradeoff} ## Alternatives Considered ### {Alternative 1} - Rejected because: {reason} ``` -
documentation-patterns.md 6.9 KB
# Documentation Patterns ## Contents - [@moduledoc Templates](#moduledoc-templates) - [@doc Templates](#doc-templates) - [ADR Template](#adr-template) - [README Section Template](#readme-section-template) ## @moduledoc Templates ### Context Module ```elixir defmodule MyApp.Accounts do @moduledoc """ The Accounts context manages user registration, authentication, and profile management. This context is the public API for all user-related operations. Controllers and LiveViews should call functions here rather than accessing schemas directly. ## Functions ### Registration * `register_user/1` - Creates a new user account * `confirm_user/1` - Confirms email address ### Authentication * `authenticate_user/2` - Validates credentials * `create_session/1` - Creates a new session ## Examples iex> Accounts.register_user(%{email: "user@example.com", password: "secret123"}) {:ok, %User{}} iex> Accounts.authenticate_user("user@example.com", "wrong") {:error, :invalid_credentials} """ end ``` ### Schema Module ```elixir defmodule MyApp.Accounts.User do @moduledoc """ Schema representing a user account. ## Fields * `email` - User's email address (unique, required) * `password_hash` - Argon2 hashed password * `confirmed_at` - When email was confirmed (nil if unconfirmed) * `role` - User role: `:member` | `:admin` ## Changesets * `registration_changeset/2` - For new user registration * `password_changeset/2` - For password changes * `email_changeset/2` - For email changes ## Associations * `posts` - Has many posts * `comments` - Has many comments """ end ``` ### LiveView Module ```elixir defmodule MyAppWeb.UserRegistrationLive do @moduledoc """ LiveView for user registration. ## Assigns * `form` - The registration form changeset * `trigger_submit` - Whether to trigger form submission ## Events * `"save"` - Submits registration form * `"validate"` - Validates form on change ## Example live "/users/register", UserRegistrationLive, :new """ end ``` ### GenServer Module ```elixir defmodule MyApp.RateLimiter do @moduledoc """ GenServer that tracks request rates per IP address. ## Why a GenServer? This uses a GenServer (rather than ETS or Agent) because: - Needs periodic cleanup of expired entries (handle_info) - Coordinates with external rate limit service - Requires atomic check-and-increment operations ## State Map of IP addresses to request counts and timestamps: %{ {192, 168, 1, 1} => %{count: 5, window_start: ~U[...]}, ... } ## Configuration config :my_app, MyApp.RateLimiter, max_requests: 100, window_seconds: 60 ## Usage case RateLimiter.check("192.168.1.1") do :ok -> proceed() {:error, :rate_limited} -> return_429() end """ end ``` ### Oban Worker ```elixir defmodule MyApp.Workers.SendEmailWorker do @moduledoc """ Oban worker for sending emails asynchronously. ## Idempotency Uses `email_id` as idempotency key. Safe to retry - checks if email already sent before processing. ## Args * `"email_id"` - ID of the Email record to send * `"template"` - Email template name ## Queues Runs on `:mailers` queue with rate limiting. ## Example %{email_id: 123, template: "welcome"} |> SendEmailWorker.new() |> Oban.insert() """ end ``` ## @doc Templates ### Context Function ```elixir @doc """ Creates a magic link token for passwordless authentication. Generates a secure random token, stores it in the database with an expiration time, and returns the token for inclusion in an email link. ## Parameters * `user` - The user to create a token for * `opts` - Options * `:expires_in` - Token lifetime in seconds (default: 86400) ## Returns * `{:ok, token}` - The magic link token string * `{:error, changeset}` - If token creation fails ## Examples iex> Auth.create_magic_token(user) {:ok, "abc123..."} iex> Auth.create_magic_token(user, expires_in: 3600) {:ok, "def456..."} """ @spec create_magic_token(User.t(), keyword()) :: {:ok, String.t()} | {:error, Ecto.Changeset.t()} def create_magic_token(user, opts \\ []) ``` ### Query Function ```elixir @doc """ Lists users matching the given criteria. ## Parameters * `criteria` - Keyword list of filters * `:role` - Filter by role * `:confirmed` - Filter by confirmation status * `:search` - Search in email/name * `opts` - Pagination options * `:page` - Page number (default: 1) * `:per_page` - Items per page (default: 20) ## Returns A list of users (may be empty). ## Examples iex> Accounts.list_users(role: :admin) [%User{role: :admin}, ...] iex> Accounts.list_users(search: "john", page: 2) [%User{}, ...] """ ``` ### LiveView Event Handler ```elixir @doc """ Handles the "save" event from the registration form. Attempts to register the user. On success, redirects to confirmation page. On failure, re-renders form with errors. ## Parameters * `params` - Form parameters with "user" key * `socket` - LiveView socket ## Returns Updated socket, either: * Redirected to confirmation page on success * Re-rendered with form errors on failure """ def handle_event("save", %{"user" => params}, socket) ``` ## ADR Template ```markdown # ADR-{number}: {Title} **Date**: YYYY-MM-DD **Status**: Proposed | Accepted | Deprecated | Superseded by ADR-X **Deciders**: {who made the decision} **Technical Story**: {link to issue/PR if applicable} ## Context and Problem Statement {Describe the context and problem in 2-3 sentences. What forces are at play? What decision needs to be made?} ## Decision Drivers * {driver 1, e.g., performance requirement} * {driver 2, e.g., team familiarity} * {driver 3, e.g., maintenance burden} ## Considered Options 1. {Option 1} 2. {Option 2} 3. {Option 3} ## Decision Outcome Chosen option: **"{Option X}"**, because {justification}. ### Positive Consequences * {positive consequence 1} * {positive consequence 2} ### Negative Consequences * {negative consequence 1} * {mitigation for negative consequence} ## Pros and Cons of the Options ### {Option 1} {Description} * Good, because {argument a} * Good, because {argument b} * Bad, because {argument c} ### {Option 2} {Description} * Good, because {argument a} * Bad, because {argument b} * Bad, because {argument c} ## Links * {Link to related ADR} * {Link to relevant documentation} * {Link to discussion/issue} ``` ## README Section Template ````markdown ## {Feature Name} {One paragraph description of what this feature does and why it exists.} ### Configuration ```elixir config :my_app, :feature_name, option_a: "value", option_b: 123 ``` ### Usage ```elixir MyApp.Feature.do_thing() ``` ### Troubleshooting **Problem**: {Common issue} **Solution**: {How to fix} ```` -
output-format.md 803 B
# Documentation Output Format Write documentation report to `.claude/plans/{slug}/reviews/{feature}-docs.md`: ```markdown # Documentation: {Feature} ## Generated Documentation ### @moduledoc Added | Module | Description | |--------|-------------| | `MyApp.Auth` | Authentication context | | `MyApp.Auth.MagicToken` | Magic token schema | ### @doc Added | Function | Module | |----------|--------| | `create_magic_token/1` | MyApp.Auth | | `verify_magic_token/1` | MyApp.Auth | ### README Updated - Added "Magic Link Authentication" section ### ADR Created - `docs/adr/003-magic-link-auth.md` ## Documentation Checklist - [x] All new modules have @moduledoc - [x] All public functions have @doc - [x] README updated for user-facing features - [x] ADR created for architectural decisions ```
-
-
SKILL.md 2.9 KB
--- name: phx-document description: 'Use when asked to document Elixir code: add or fill in @moduledoc and @doc for modules and functions. Documents tested code only; may add a README section or ADR. Not for docs lookup or audits.' --- # Document Generate documentation for newly implemented features. ## Usage ``` phx-document .claude/plans/magic-link-auth/plan.md phx-document magic link authentication phx-document # Auto-detect from recent plan ``` ## Iron Laws 1. **Never remove existing documentation** — Existing docs may reflect design intent that isn't obvious from code alone; update rather than replace 2. **@moduledoc on every public module** — Undocumented modules accumulate quickly and create onboarding friction for new team members 3. **ADRs capture the "why", not the "what"** — Code shows what was built; ADRs explain why this approach was chosen over alternatives 4. **Match @doc to function's public API** — Document parameters, return values, and edge cases; callers shouldn't need to read the implementation 5. **DO NOT add @doc to untested code** — documentation implies a stable contract; document only after tests confirm the function behaves as described ## What Gets Documented | Output | Description | |--------|-------------| | `@moduledoc` | For new modules missing documentation | | `@doc` | For public functions without docs | | README section | For user-facing features | | ADR | For significant architectural decisions | ## Workflow ### Step 0: Pre-check (avoid no-op runs) Run `git diff --name-only HEAD~5 | grep '\.ex$' | head -20` to check for new `.ex` files. If no new `.ex` files were added (only modifications), skip the full audit and report: "No new modules — documentation coverage unchanged." A full audit of unchanged coverage produces nothing to add. 1. **Identify** new modules from recent commits or plan file 2. **Check** documentation coverage (`@moduledoc`, `@doc`) 3. **Generate** missing docs using templates 4. **Add** README section if user-facing feature 5. **Create** ADR if architectural decision was made 6. **Write** report to `.claude/plans/{slug}/reviews/{feature}-docs.md` ## When to Generate ADRs | Trigger | Create ADR | |---------|-----------| | New external dependency | Yes | | New database table | Maybe (if schema non-obvious) | | New OTP process | Yes (explain why process needed) | | New context | Maybe (if boundaries non-obvious) | | New auth mechanism | Yes | | Performance optimization | Yes | ## Integration with Workflow ```text phx-plan → phx-work → phx-review ↓ phx-document ← YOU ARE HERE (optional, suggested after review passes) ``` ## References - `references/doc-templates.md` — @moduledoc, @doc, README, ADR templates - `references/output-format.md` — Documentation report format - `references/doc-best-practices.md` — Elixir documentation best practices - `references/documentation-patterns.md` — Detailed documentation patterns
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.