Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions .agents/skills/accessibility/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
---
name: accessibility
description: |
WCAG 2.2 AA accessibility standards for the Exceptionless frontend. Semantic HTML, keyboard
navigation, ARIA patterns, focus management, and form accessibility.
Keywords: WCAG, accessibility, a11y, ARIA, semantic HTML, keyboard navigation, focus management,
screen reader, alt text, aria-label, aria-describedby, skip links, focus trap
description: >
Use this skill when building or reviewing frontend components for accessibility compliance.
Covers WCAG 2.2 AA standards including semantic HTML, keyboard navigation, ARIA patterns,
focus management, screen reader support, and form accessibility. Apply when creating new
UI components, fixing accessibility bugs, adding skip links or focus traps, or ensuring
inclusive markup — even if the user doesn't explicitly mention "a11y" or "WCAG."
---

# Accessibility (WCAG 2.2 AA)
Expand Down Expand Up @@ -110,7 +111,7 @@ description: |
// When dialog opens, focus first interactive element
$effect(() => {
if (open) {
dialogRef?.querySelector('input, button')?.focus();
dialogRef?.querySelector("input, button")?.focus();
}
});

Expand Down Expand Up @@ -240,10 +241,10 @@ npm run test:e2e

```typescript
// In Playwright tests
import AxeBuilder from '@axe-core/playwright';
import AxeBuilder from "@axe-core/playwright";

test('page is accessible', async ({ page }) => {
await page.goto('/dashboard');
test("page is accessible", async ({ page }) => {
await page.goto("/dashboard");
const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
});
Expand Down
10 changes: 5 additions & 5 deletions .agents/skills/backend-architecture/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
name: backend-architecture
description: |
Backend architecture for Exceptionless. Project layering, repositories, validation,
controllers, authorization, WebSockets, configuration, and Aspire orchestration.
Keywords: Core, Insulation, repositories, FluentValidation, MiniValidator, controllers,
AuthorizationRoles, ProblemDetails, Aspire, WebSockets, AppOptions
description: >
Use this skill when working on the ASP.NET Core backend — adding controllers, repositories,
validators, authorization, WebSocket endpoints, or Aspire orchestration. Apply when modifying
project layering (Core, Insulation, Web, Job), configuring services, returning ProblemDetails
errors, or understanding how the backend is structured.
---

# Backend Architecture
Expand Down
10 changes: 5 additions & 5 deletions .agents/skills/backend-testing/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
name: backend-testing
description: |
Backend testing with xUnit, Foundatio.Xunit, integration tests with AppWebHostFactory,
FluentClient, ProxyTimeProvider for time manipulation, and test data builders.
Keywords: xUnit, Fact, Theory, integration tests, AppWebHostFactory, FluentClient,
ProxyTimeProvider, TimeProvider, Foundatio.Xunit, TestWithLoggingBase, test data builders
description: >
Use this skill when writing or modifying C# tests — unit tests, integration tests, or
test fixtures. Covers xUnit patterns, AppWebHostFactory for integration testing, FluentClient
for API assertions, ProxyTimeProvider for time manipulation, and test data builders. Apply
when adding new test cases, debugging test failures, or setting up test infrastructure.
---

# Backend Testing
Expand Down
10 changes: 5 additions & 5 deletions .agents/skills/dotnet-cli/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
name: dotnet-cli
description: |
.NET command-line tools for building, testing, and formatting. Common dotnet commands
and development workflow.
Keywords: dotnet build, dotnet restore, dotnet test, dotnet format, dotnet run,
NuGet, package restore, CLI commands, build system
description: >
Use this skill when running .NET CLI commands — building, testing, restoring packages,
formatting code, or running projects. Covers dotnet build, test, restore, format, run,
and NuGet package management. Apply when troubleshooting build errors, running the backend,
or executing any dotnet command-line operation.
---

# .NET CLI
Expand Down
10 changes: 5 additions & 5 deletions .agents/skills/dotnet-conventions/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
name: dotnet-conventions
description: |
C# coding standards for the Exceptionless codebase. Naming conventions, async patterns,
structured logging, nullable reference types, and formatting rules.
Keywords: C# style, naming conventions, _camelCase, PascalCase, async suffix,
CancellationToken, nullable annotations, structured logging, ExceptionlessState
description: >
Use this skill when writing or reviewing C# code to follow project conventions. Covers
naming standards, async patterns, CancellationToken usage, structured logging, nullable
reference types, and formatting rules. Apply when authoring new C# classes, reviewing
code style, or ensuring consistency with existing patterns.
---

# .NET Conventions
Expand Down
78 changes: 39 additions & 39 deletions .agents/skills/e2e-testing/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
name: e2e-testing
description: |
End-to-end frontend testing with Playwright. Page Object Model, selectors, fixtures,
accessibility audits. Limited E2E coverage currently - area for improvement.
Keywords: Playwright, E2E, Page Object Model, POM, data-testid, getByRole, getByLabel,
getByText, fixtures, axe-playwright, frontend testing
description: >
Use this skill when writing or running end-to-end browser tests with Playwright. Covers
Page Object Model patterns, selector strategies (data-testid, getByRole, getByLabel),
fixtures, and accessibility audits with axe-playwright. Apply when adding E2E test coverage,
debugging flaky tests, or testing user flows through the browser.
---

# E2E Testing (Frontend)
Expand All @@ -24,7 +24,7 @@ Create page objects for reusable page interactions:

```typescript
// e2e/pages/login-page.ts
import { type Page, type Locator, expect } from '@playwright/test';
import { type Page, type Locator, expect } from "@playwright/test";

export class LoginPage {
readonly page: Page;
Expand All @@ -35,14 +35,14 @@ export class LoginPage {

constructor(page: Page) {
this.page = page;
this.emailInput = page.getByLabel('Email');
this.passwordInput = page.getByLabel('Password');
this.submitButton = page.getByRole('button', { name: /log in/i });
this.errorMessage = page.getByRole('alert');
this.emailInput = page.getByLabel("Email");
this.passwordInput = page.getByLabel("Password");
this.submitButton = page.getByRole("button", { name: /log in/i });
this.errorMessage = page.getByRole("alert");
}

async goto() {
await this.page.goto('/login');
await this.page.goto("/login");
}

async login(email: string, password: string) {
Expand All @@ -61,26 +61,26 @@ export class LoginPage {

```typescript
// e2e/auth/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/login-page';
import { test, expect } from "@playwright/test";
import { LoginPage } from "../pages/login-page";

test.describe('Login', () => {
test('successful login redirects to dashboard', async ({ page }) => {
test.describe("Login", () => {
test("successful login redirects to dashboard", async ({ page }) => {
const loginPage = new LoginPage(page);

await loginPage.goto();
await loginPage.login('user@example.com', 'password123');
await loginPage.login("user@example.com", "password123");

await expect(page).toHaveURL('/');
await expect(page).toHaveURL("/");
});

test('invalid credentials shows error', async ({ page }) => {
test("invalid credentials shows error", async ({ page }) => {
const loginPage = new LoginPage(page);

await loginPage.goto();
await loginPage.login('wrong@example.com', 'wrongpassword');
await loginPage.login("wrong@example.com", "wrongpassword");

await loginPage.expectError('Invalid email or password');
await loginPage.expectError("Invalid email or password");
});
});
```
Expand All @@ -89,24 +89,24 @@ test.describe('Login', () => {

1. **Semantic selectors first**:

```typescript
page.getByRole('button', { name: /submit/i });
page.getByLabel('Email address');
page.getByText('Welcome back');
```
```typescript
page.getByRole("button", { name: /submit/i });
page.getByLabel("Email address");
page.getByText("Welcome back");
```

2. **Fallback to test IDs**:

```typescript
page.getByTestId('stack-trace');
```
```typescript
page.getByTestId("stack-trace");
```

3. **Avoid implementation details**:

```typescript
// ❌ Avoid CSS classes and IDs
page.locator('.btn-primary');
```
```typescript
// ❌ Avoid CSS classes and IDs
page.locator(".btn-primary");
```

## Backend Data Setup

Expand All @@ -122,13 +122,13 @@ For tests requiring specific data, consider:
```typescript
test.beforeEach(async ({ request }) => {
// Set up test data via API
await request.post('/api/test/seed', {
data: { scenario: 'events-with-errors' }
await request.post("/api/test/seed", {
data: { scenario: "events-with-errors" },
});
});

test.afterEach(async ({ request }) => {
await request.delete('/api/test/cleanup');
await request.delete("/api/test/cleanup");
});
```

Expand All @@ -137,11 +137,11 @@ test.afterEach(async ({ request }) => {
## Accessibility Audits

```typescript
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
import { test, expect } from "@playwright/test";
import AxeBuilder from "@axe-core/playwright";

test('login page has no accessibility violations', async ({ page }) => {
await page.goto('/login');
test("login page has no accessibility violations", async ({ page }) => {
await page.goto("/login");

const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
Expand Down
10 changes: 5 additions & 5 deletions .agents/skills/foundatio/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
name: foundatio
description: |
Foundatio infrastructure abstractions for caching, queuing, messaging, file storage,
locking, jobs, and resilience. Use context7 for complete API documentation.
Keywords: Foundatio, ICacheClient, IQueue, IMessageBus, IFileStorage, ILockProvider,
IJob, QueueJobBase, resilience, retry, Redis, Elasticsearch
description: >
Use this skill when working with Foundatio infrastructure abstractions caching, queuing,
messaging, file storage, locking, or background jobs. Apply when using ICacheClient, IQueue,
IMessageBus, IFileStorage, ILockProvider, or IJob, or when implementing retry/resilience
patterns. Covers both in-memory and production (Redis, Elasticsearch) implementations.
---

# Foundatio
Expand Down
52 changes: 30 additions & 22 deletions .agents/skills/frontend-architecture/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
name: frontend-architecture
description: |
Svelte SPA architecture for Exceptionless. Route groups, lib structure, API client,
feature slices, and barrel exports.
Keywords: route groups, $lib, feature slices, api-client, barrel exports, index.ts,
vertical slices, shared components, generated models, ClientApp structure
description: >
Use this skill when working on the Svelte SPA's project structure — adding routes, creating
feature slices, organizing shared components, or understanding the ClientApp directory layout.
Covers route groups, $lib conventions, barrel exports, API client organization, and vertical
slice architecture. Apply when deciding where to place new files or components.
---

# Frontend Architecture
Expand Down Expand Up @@ -80,20 +80,25 @@ Centralize API calls per feature:

```typescript
// features/organizations/api.svelte.ts
import { createQuery, createMutation, useQueryClient } from '@tanstack/svelte-query';
import { useFetchClient } from '@exceptionless/fetchclient';
import type { Organization, CreateOrganizationRequest } from './models';
import {
createQuery,
createMutation,
useQueryClient,
} from "@tanstack/svelte-query";
import { useFetchClient } from "@exceptionless/fetchclient";
import type { Organization, CreateOrganizationRequest } from "./models";

export function getOrganizationsQuery() {
const client = useFetchClient();

return createQuery(() => ({
queryKey: ['organizations'],
queryKey: ["organizations"],
queryFn: async () => {
const response = await client.getJSON<Organization[]>('/organizations');
const response =
await client.getJSON<Organization[]>("/organizations");
if (!response.ok) throw response.problem;
return response.data!;
}
},
}));
}

Expand All @@ -103,13 +108,16 @@ export function postOrganizationMutation() {

return createMutation(() => ({
mutationFn: async (data: CreateOrganizationRequest) => {
const response = await client.postJSON<Organization>('/organizations', data);
const response = await client.postJSON<Organization>(
"/organizations",
data,
);
if (!response.ok) throw response.problem;
return response.data!;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['organizations'] });
}
queryClient.invalidateQueries({ queryKey: ["organizations"] });
},
}));
}
```
Expand All @@ -123,8 +131,8 @@ Re-export generated models through feature model folders:
export type {
Organization,
CreateOrganizationRequest,
UpdateOrganizationRequest
} from '$lib/generated';
UpdateOrganizationRequest,
} from "$lib/generated";

// Add feature-specific types
export interface OrganizationWithStats extends Organization {
Expand All @@ -139,9 +147,9 @@ Use `index.ts` for clean imports:

```typescript
// features/organizations/index.ts
export { getOrganizationsQuery, postOrganizationMutation } from './api.svelte';
export type { Organization, CreateOrganizationRequest } from './models';
export { organizationSchema } from './schemas';
export { getOrganizationsQuery, postOrganizationMutation } from "./api.svelte";
export type { Organization, CreateOrganizationRequest } from "./models";
export { organizationSchema } from "./schemas";
```

## Shared Components
Expand Down Expand Up @@ -176,9 +184,9 @@ Prefer regeneration over hand-writing DTOs. Generated types live in `$lib/genera

```typescript
// Configured in svelte.config.js
import { Button } from '$comp/ui/button'; // $lib/components
import { User } from '$features/users/models'; // $lib/features
import { formatDate } from '$shared/formatters'; // $lib/features/shared
import { Button } from "$comp/ui/button"; // $lib/components
import { User } from "$features/users/models"; // $lib/features
import { formatDate } from "$shared/formatters"; // $lib/features/shared
```

## Composite Component Pattern
Expand Down
Loading
Loading