# Working with AI agents

> AppointMe ships CLAUDE.md files and Claude Code skills that teach coding agents its .NET slice, value object and API client rules. Use them, extend them, and feed agents these docs.

Source page: https://appointme.dev/docs/guides/ai-assisted-development/

* **You'll get:** An agent that follows AppointMe’s conventions
* **Time:** 15 minutes
* **Touches:** `CLAUDE.md`, `.claude/skills/`, a new `AGENTS.md`
* **Before you start:** Claude Code or another coding agent

## What ships for agents

**Two `CLAUDE.md` files and three skills tell an agent how the codebase is built and which commands prove a change works.**

| File                                            | Scope            | Tells the agent                                                                                                                                |
| ----------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `CLAUDE.md`                                     | The whole repo   | Build, test and run commands, module layout, slice rules, value object factories, naming rules, when to regenerate the API client, local ports |
| `src/AppointMe.Frontend/CLAUDE.md`              | The frontend     | Vite+ commands, folder layout, data fetching, modal dialogs, forms, date and time rules, and a list of “don’t”s                                |
| `.claude/skills/regenerate-api/SKILL.md`        | Contract changes | Build the API, run orval, report the `src/api/` diff, then type-check the call sites                                                           |
| `.claude/skills/verify/SKILL.md`                | Runtime checks   | Start Aspire, sign in through the demo login with `curl`, send the antiforgery header on mutations                                             |
| `src/AppointMe.Frontend/.claude/skills/shadcn/` | UI components    | The shadcn/ui CLI and its composition rules, vendored from `shadcn/ui` (recorded in `skills-lock.json`)                                        |
| `src/AppointMe.Frontend/.agents/skills/shadcn/` | UI components    | An identical copy, for agents that read `.agents/skills/`                                                                                      |

There’s no `AGENTS.md`. Every file above also ships in a project generated with `dotnet new`, renamed to your project. Only `.claude/settings.json`, upstream’s plugin settings, is left out.

### What the two root skills do

| Skill             | Use it when                                                                                | It runs                                                                                                                                              |
| ----------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/regenerate-api` | An endpoint, request or response shape, route, verb, status code or auth attribute changed | `npm run generate:api`, which builds `AppointMe.Api` in Release (writing `obj/openapi.json`) and runs orval. Then `npx vp check --no-fmt --no-lint`. |
| `/verify`         | A change needs checking against the running app                                            | `dotnet run` in `src/AppointMe.Aspire`, then `curl -sk` calls against `https://localhost:7233` with a demo-login cookie jar                          |

`regenerate-api` says when *not* to run it: internal handlers, domain events, EF configuration, migrations and tests don’t change the contract. Neither skill commits anything.

## Use them with Claude Code

**Start Claude Code at the repo root. The root `CLAUDE.md` loads at launch, and the frontend’s loads the first time the agent touches a frontend file.**

| File                               | When Claude Code loads it                                                                                                   |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `CLAUDE.md`                        | At launch. Claude Code reads every `CLAUDE.md` from the working directory up, so starting in a subfolder loads it too.      |
| `src/AppointMe.Frontend/CLAUDE.md` | The first time Claude reads or edits a file under `src/AppointMe.Frontend/`, or at launch if you start there                |
| `regenerate-api`, `verify`         | At launch. Type `/regenerate-api` or `/verify`, or let Claude choose one from its description.                              |
| The frontend’s `shadcn` skill      | The first time Claude touches a frontend file. It’s `user-invocable: false`, so Claude invokes it and there’s no `/shadcn`. |

The shadcn skill’s frontmatter also pre-approves its CLI commands for the turn that uses it:

src/AppointMe.Frontend/.claude/skills/shadcn/SKILL.md

```md
---
name: shadcn
description: Manages shadcn components and projects — adding, searching, fixing, debugging, styling, and composing UI. Provides project context, component docs, and usage examples. Applies when working with shadcn/ui, component registries, presets, --preset codes, or any project with a components.json file. Also triggers for "shadcn init", "create an app with --preset", or "switch to --preset".
user-invocable: false
allowed-tools: Bash(npx shadcn@latest *), Bash(pnpm dlx shadcn@latest *), Bash(bunx --bun shadcn@latest *)
---
```

To see which instruction files a session loaded, run `/context all` and check **Memory files**. The loading rules are in Claude Code’s [memory](https://code.claude.com/docs/en/memory) and [skills](https://code.claude.com/docs/en/skills) docs.

## Use them with other agents

**Agents that follow the [AGENTS.md](https://agents.md) convention look for `AGENTS.md`, which the repo doesn’t have. Add one that every tool shares.**

| Option                                        | How                                                                                   | Trade-off                                                                       |
| --------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Move the content to `AGENTS.md` and import it | `git mv CLAUDE.md AGENTS.md`, then a new `CLAUDE.md` whose first line is `@AGENTS.md` | One shared file. Claude-only notes go below the import.                         |
| Symlink                                       | `ln -s CLAUDE.md AGENTS.md`                                                           | Nothing to keep in sync, but symlinks need `core.symlinks` on Windows checkouts |
| Repeat for the frontend                       | The same in `src/AppointMe.Frontend/`                                                 | Agents that read nested `AGENTS.md` files get the frontend rules too            |

Claude Code reads `AGENTS.md` itself only when there’s no `CLAUDE.md`. AppointMe has one, so keep the `@AGENTS.md` import if you take the first option.

Skills follow the [Agent Skills](https://agentskills.io) standard, so each `SKILL.md` is a plain Markdown procedure that any agent can follow. The root skills exist only under `.claude/skills/`, and Claude Code doesn’t read `.agents/`. Copy them to wherever your agent looks, or list them in `AGENTS.md`. The shadcn skill’s `` !`npx shadcn@latest info --json` `` line is a Claude Code extension: Claude Code runs the command when the skill loads, and other agents see it as text.

## Get good results in this codebase

**Ask for changes in the codebase’s own terms, point at the slice to copy, and name the check that proves the change.**

| Ask the agent to                                                                                                       | Because                                                                                                                        | Check                                                            |
| ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- |
| Put each use case in its own verb-first slice folder, modeled on an existing one such as `Customers/RegisterCustomer/` | That’s the layout `CLAUDE.md` teaches. See [Vertical slices](/docs/concepts/vertical-slices/).                                 | The new folder holds the same kinds of file as the one it copies |
| Build value objects from input with `Create` or `CreateOrNull`, never `new`                                            | The primary constructors don’t validate. They’re for EF Core and Dapper.                                                       | Search the diff for `new Email(` and `new PersonName(`           |
| Run `/regenerate-api` in the same task as any contract change                                                          | Otherwise the committed client drifts from the API                                                                             | `git diff --stat src/AppointMe.Frontend/src/api/` isn’t empty    |
| Run `codegen test` after adding or changing a Wolverine handler                                                        | In Development, Wolverine generates handler code at runtime, so a broken handler fails only when a message reaches it          | It prints `Success!`                                             |
| Gate new startup code that needs a database or secrets on `CodeGenerationDetection.IsRunningGeneration()`              | Building the API runs its startup to write `obj/openapi.json`                                                                  | `dotnet build` passes                                            |
| Take `CompanyId` and `IPrincipal` in handlers, and call `principal.Require(...)`                                       | That’s how the [handlers get their tenant](/docs/concepts/multi-tenancy/#handlers-companyid-is-injected) and check permissions | Code review                                                      |
| Filter every Dapper query on `[CompanyId] = @CompanyId`                                                                | Nothing enforces it. See [Multi-tenancy](/docs/concepts/multi-tenancy/#reads-dapper-carries-the-predicate).                    | Code review                                                      |

This is the factory rule as the code applies it, and the example `CLAUDE.md` points agents at:

src/CRM/AppointMe.Crm/Customers/RegisterCustomer/RegisterCustomerCommandHandler.cs

```cs
var customer = Customer.Register(
    companyId: companyId,
    name: PersonName.Create(command.FirstName, command.LastName),
    dateOfBirth: DateOfBirth.CreateOrNull(command.DateOfBirth, timeProvider),
    gender: Gender.ParseOrNull(command.Gender),
    email: Email.CreateOrNull(command.Email),
    registrationDate: timeProvider.GetUtcNow()
);
```

The Wolverine check is the one CI runs. It needs no database, because the `codegen` argument switches the app to the `Codegen` environment:

```bash
dotnet run --project src/AppointMe.Api/AppointMe.Api.csproj --no-launch-profile -- codegen test
```

A good prompt names all three things: *“Add a DeactivateCustomer slice to CRM, modeled on `Customers/DeleteCustomer/`. Build value objects with their factories. It changes the API, so run /regenerate-api and fix the call sites. Finish with `dotnet test` and `npm run check`.”*

### Why tell the agent which slice to copy?

`CLAUDE.md` describes the rules, but the slices are the ground truth: file names, the extension-method factory, how the endpoint maps the request to a command. An agent that reads one finished slice first copies all of that, including what the prose leaves out. Without one, it has only the prose to go on, plus whatever layout it has seen most often elsewhere.

## Extend CLAUDE.md for your product

**Treat `CLAUDE.md` like code. Delete what doesn’t apply to you, fix what’s stale, and keep your own rules in files upstream doesn’t edit.**

1. **Delete what’s only for the upstream repo.** In a generated project, the “Template package” subsection describes `templates/` and `.template.config/`, which aren’t there (the rename turns its heading into `dotnet new contoso.booking`). The nuget.org lines under “Releasing” don’t apply to you either.

2. **Fix the lines that lag the code.** The table below lists them. An agent follows `CLAUDE.md` over its own reading of the code, so a stale line costs you every session.

3. **Put your product’s rules in `.claude/rules/`.** Files there load like `CLAUDE.md`, and a `paths:` field scopes one to part of the tree. Keeping them out of upstream’s `CLAUDE.md` makes [upstream merges](/docs/guides/upstream-updates/) easier.

   Example: .claude/rules/invoicing.md

   ```md
   ---
   paths:
     - "src/Invoicing/**"
   ---


   # Invoicing module


   - `InvoicingDbContext` uses the `invoicing` schema. Add migrations to it only.
   - Other modules reference `Contoso.Booking.Invoicing.Contracts`, never `Contoso.Booking.Invoicing`.
   - Every Dapper query filters on `[CompanyId] = @CompanyId`.
   ```

4. **Turn repeated procedures into skills.** Copy the shape of `.claude/skills/regenerate-api/SKILL.md`: a `description` that says when to use the skill, then preconditions, steps, pitfalls and what’s out of scope.

5. **Check what loads.** Start a new session, run `/context all`, and confirm your files are listed. Claude Code’s docs suggest keeping each `CLAUDE.md` under 200 lines.

### Lines that lag the code

| `CLAUDE.md` says                                                       | The code at this commit                                                            |
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `.AddCompaniesModule()` in the module registration example             | `.AddOrganizationsModule()`, and `.AddBookingModule()` is missing from the example |
| `Booking/AppointMe.Booking.Contracts/` with “Booking contracts (TBD)”  | Booking is a full module: `src/Booking/AppointMe.Booking`                          |
| Bounded contexts: Identity, Organizations, CRM                         | There’s also `BookingDbContext`, with the `booking` schema                         |
| `CustomerRegisteredEventHandler` sits in `CreateAttendee/`             | It’s in `Attendees/ReconcileAttendees/`                                            |
| Keycloak at `http://localhost:8082` (the `verify` skill says the same) | Keycloak serves `https://localhost:8082` once the dev certificate is trusted       |

## Feed these docs to an agent

**These docs publish machine-readable copies, so you can give an agent the whole set or a single page as Markdown.**

| URL                                              | What it holds                                                                                               |
| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| `https://appointme.dev/docs/llms.txt`            | An index for language models, linking the two sets below                                                    |
| `https://appointme.dev/docs/llms-full.txt`       | Every page in one file                                                                                      |
| `https://appointme.dev/docs/llms-small.txt`      | An abridged version, with non-essential content removed                                                     |
| `https://appointme.dev/docs/<section>/<page>.md` | One page as Markdown, for example [this page](https://appointme.dev/docs/guides/ai-assisted-development.md) |

Each page’s right rail also has **Copy page as Markdown** and **View as Markdown**. The docs are checked against a fixed commit of upstream `main`. When they disagree with the code in your repository, tell the agent to trust the code.

Give the agent the concept page, not the whole set

For a task in one area, a single page such as [Multi-tenancy](https://appointme.dev/docs/concepts/multi-tenancy.md) is usually enough. It leaves more of the context window for your code.

## Troubleshooting

| Symptom                                                    | Cause                                                                           | Fix                                                                              |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| The agent calls `.AddCompaniesModule()`                    | A stale example in `CLAUDE.md`                                                  | Fix the line, as in [Lines that lag the code](#lines-that-lag-the-code)          |
| The agent hand-edits `src/api/client.ts`                   | It missed the regeneration rule                                                 | Run `/regenerate-api`. Orval’s `clean: true` overwrites hand edits anyway.       |
| `npm run generate:api` fails in `GenerateOpenApiDocuments` | New startup code needs a database or real configuration                         | Gate it on `CodeGenerationDetection.IsRunningGeneration()`                       |
| The agent ignores frontend rules                           | It hasn’t touched a frontend file yet, so the frontend `CLAUDE.md` isn’t loaded | Ask it to read `src/AppointMe.Frontend/CLAUDE.md` first                          |
| `/shadcn` isn’t in the menu                                | The skill is `user-invocable: false`                                            | Ask for the UI change, and Claude loads the skill itself                         |
| Another agent ignores every convention                     | It reads `AGENTS.md`, and there isn’t one                                       | Add one, as in [Use them with other agents](#use-them-with-other-agents)         |
| A `verify` mutation returns `Antiforgery Validation Error` | The `X-XSRF-TOKEN` header is missing or stale                                   | Send the `XSRF-TOKEN` cookie’s value, and refresh it with a GET after logging in |

## Next

* [Vertical slices](/docs/concepts/vertical-slices/) (Concept)
* [Add an endpoint](/docs/guides/add-an-endpoint/) (Guide)
* [IDs & value objects](/docs/concepts/ids-and-value-objects/) (Concept)
