Skip to content
StarLive demo →
GuideChecked against main · 31 Oct 20269 min read

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.

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

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.

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.

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.mdView source ↗
---
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 and skills docs.

Agents that follow the 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 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.

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. 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 and check permissions Code review
Filter every Dapper query on [CompanyId] = @CompanyId Nothing enforces it. See Multi-tenancy. 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.csView source ↗
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:

Terminal window
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?1 min · optional

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.

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 easier.

    Example: .claude/rules/invoicing.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.

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

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

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.

Symptom Cause Fix
The agent calls .AddCompaniesModule() A stale example in CLAUDE.md Fix the line, as in 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
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