diff --git a/.agents/skills/accessibility/SKILL.md b/.agents/skills/accessibility/SKILL.md index 48d554104..0db396127 100644 --- a/.agents/skills/accessibility/SKILL.md +++ b/.agents/skills/accessibility/SKILL.md @@ -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) @@ -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(); } }); @@ -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([]); }); diff --git a/.agents/skills/backend-architecture/SKILL.md b/.agents/skills/backend-architecture/SKILL.md index 29e09c15d..c77118046 100644 --- a/.agents/skills/backend-architecture/SKILL.md +++ b/.agents/skills/backend-architecture/SKILL.md @@ -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 diff --git a/.agents/skills/backend-testing/SKILL.md b/.agents/skills/backend-testing/SKILL.md index 0afdd6582..08acef96e 100644 --- a/.agents/skills/backend-testing/SKILL.md +++ b/.agents/skills/backend-testing/SKILL.md @@ -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 diff --git a/.agents/skills/dotnet-cli/SKILL.md b/.agents/skills/dotnet-cli/SKILL.md index 289015780..b20841d86 100644 --- a/.agents/skills/dotnet-cli/SKILL.md +++ b/.agents/skills/dotnet-cli/SKILL.md @@ -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 diff --git a/.agents/skills/dotnet-conventions/SKILL.md b/.agents/skills/dotnet-conventions/SKILL.md index f04363bcf..540bbf10e 100644 --- a/.agents/skills/dotnet-conventions/SKILL.md +++ b/.agents/skills/dotnet-conventions/SKILL.md @@ -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 diff --git a/.agents/skills/e2e-testing/SKILL.md b/.agents/skills/e2e-testing/SKILL.md index 6c3e278b7..9b05e26fa 100644 --- a/.agents/skills/e2e-testing/SKILL.md +++ b/.agents/skills/e2e-testing/SKILL.md @@ -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) @@ -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; @@ -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) { @@ -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"); }); }); ``` @@ -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 @@ -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"); }); ``` @@ -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([]); diff --git a/.agents/skills/foundatio/SKILL.md b/.agents/skills/foundatio/SKILL.md index e93930f4d..72ed37ec8 100644 --- a/.agents/skills/foundatio/SKILL.md +++ b/.agents/skills/foundatio/SKILL.md @@ -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 diff --git a/.agents/skills/frontend-architecture/SKILL.md b/.agents/skills/frontend-architecture/SKILL.md index b6609daa0..3fb9a9ae6 100644 --- a/.agents/skills/frontend-architecture/SKILL.md +++ b/.agents/skills/frontend-architecture/SKILL.md @@ -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 @@ -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('/organizations'); + const response = + await client.getJSON("/organizations"); if (!response.ok) throw response.problem; return response.data!; - } + }, })); } @@ -103,13 +108,16 @@ export function postOrganizationMutation() { return createMutation(() => ({ mutationFn: async (data: CreateOrganizationRequest) => { - const response = await client.postJSON('/organizations', data); + const response = await client.postJSON( + "/organizations", + data, + ); if (!response.ok) throw response.problem; return response.data!; }, onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['organizations'] }); - } + queryClient.invalidateQueries({ queryKey: ["organizations"] }); + }, })); } ``` @@ -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 { @@ -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 @@ -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 diff --git a/.agents/skills/frontend-testing/SKILL.md b/.agents/skills/frontend-testing/SKILL.md index 96c81daa1..760982642 100644 --- a/.agents/skills/frontend-testing/SKILL.md +++ b/.agents/skills/frontend-testing/SKILL.md @@ -1,9 +1,10 @@ --- name: frontend-testing -description: | - Unit and component testing for the frontend with Vitest and Testing Library. - Keywords: Vitest, @testing-library/svelte, component tests, vi.mock, render, screen, - fireEvent, userEvent, test.ts, spec.ts, describe, it, AAA pattern +description: > + Use this skill when writing or running frontend unit and component tests with Vitest and + Testing Library. Covers render/screen/fireEvent patterns, vi.mock for mocking, and the + AAA (Arrange-Act-Assert) test structure. Apply when adding test coverage for Svelte + components, debugging test failures, or setting up test utilities. --- # Frontend Testing @@ -27,10 +28,10 @@ npm run test:unit Use explicit Arrange, Act, Assert regions: ```typescript -import { describe, expect, it } from 'vitest'; +import { describe, expect, it } from "vitest"; -describe('Calculator', () => { - it('should add two numbers correctly', () => { +describe("Calculator", () => { + it("should add two numbers correctly", () => { // Arrange const a = 5; const b = 3; @@ -42,7 +43,7 @@ describe('Calculator', () => { expect(result).toBe(8); }); - it('should handle negative numbers', () => { + it("should handle negative numbers", () => { // Arrange const a = -5; const b = 3; @@ -63,11 +64,11 @@ describe('Calculator', () => { From [dates.test.ts](src/Exceptionless.Web/ClientApp/src/lib/features/shared/dates.test.ts): ```typescript -import { describe, expect, it } from 'vitest'; -import { getDifferenceInSeconds, getRelativeTimeFormatUnit } from './dates'; +import { describe, expect, it } from "vitest"; +import { getDifferenceInSeconds, getRelativeTimeFormatUnit } from "./dates"; -describe('getDifferenceInSeconds', () => { - it('should calculate difference in seconds correctly', () => { +describe("getDifferenceInSeconds", () => { + it("should calculate difference in seconds correctly", () => { // Arrange const now = new Date(); const past = new Date(now.getTime() - 5000); @@ -80,18 +81,18 @@ describe('getDifferenceInSeconds', () => { }); }); -describe('getRelativeTimeFormatUnit', () => { - it('should return correct unit for given seconds', () => { +describe("getRelativeTimeFormatUnit", () => { + it("should return correct unit for given seconds", () => { // Arrange & Act & Assert (simple value tests) - expect(getRelativeTimeFormatUnit(30)).toBe('seconds'); - expect(getRelativeTimeFormatUnit(1800)).toBe('minutes'); - expect(getRelativeTimeFormatUnit(7200)).toBe('hours'); + expect(getRelativeTimeFormatUnit(30)).toBe("seconds"); + expect(getRelativeTimeFormatUnit(1800)).toBe("minutes"); + expect(getRelativeTimeFormatUnit(7200)).toBe("hours"); }); - it('should handle boundary cases correctly', () => { + it("should handle boundary cases correctly", () => { // Arrange & Act & Assert - expect(getRelativeTimeFormatUnit(59)).toBe('seconds'); - expect(getRelativeTimeFormatUnit(60)).toBe('minutes'); + expect(getRelativeTimeFormatUnit(59)).toBe("seconds"); + expect(getRelativeTimeFormatUnit(60)).toBe("minutes"); }); }); ``` @@ -101,27 +102,27 @@ describe('getRelativeTimeFormatUnit', () => { From [cached-persisted-state.svelte.test.ts](src/Exceptionless.Web/ClientApp/src/lib/features/shared/utils/cached-persisted-state.svelte.test.ts): ```typescript -import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { CachedPersistedState } from './cached-persisted-state.svelte'; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { CachedPersistedState } from "./cached-persisted-state.svelte"; -describe('CachedPersistedState', () => { +describe("CachedPersistedState", () => { beforeEach(() => { vi.clearAllMocks(); }); - it('should initialize with default value when storage is empty', () => { + it("should initialize with default value when storage is empty", () => { // Arrange & Act - const state = new CachedPersistedState('test-key', 'default'); + const state = new CachedPersistedState("test-key", "default"); // Assert - expect(state.current).toBe('default'); + expect(state.current).toBe("default"); }); - it('should return cached value without reading storage repeatedly', () => { + it("should return cached value without reading storage repeatedly", () => { // Arrange - const getItemSpy = vi.spyOn(Storage.prototype, 'getItem'); - localStorage.setItem('test-key', 'value1'); - const state = new CachedPersistedState('test-key', 'default'); + const getItemSpy = vi.spyOn(Storage.prototype, "getItem"); + localStorage.setItem("test-key", "value1"); + const state = new CachedPersistedState("test-key", "default"); getItemSpy.mockClear(); // Act @@ -129,8 +130,8 @@ describe('CachedPersistedState', () => { const val2 = state.current; // Assert - expect(val1).toBe('value1'); - expect(val2).toBe('value1'); + expect(val1).toBe("value1"); + expect(val2).toBe("value1"); expect(getItemSpy).not.toHaveBeenCalled(); }); }); @@ -141,26 +142,44 @@ describe('CachedPersistedState', () => { From [helpers.svelte.test.ts](src/Exceptionless.Web/ClientApp/src/lib/features/events/components/filters/helpers.svelte.test.ts): ```typescript -import { describe, expect, it } from 'vitest'; -import { quoteIfSpecialCharacters } from './helpers.svelte'; +import { describe, expect, it } from "vitest"; +import { quoteIfSpecialCharacters } from "./helpers.svelte"; -describe('helpers.svelte', () => { - it('quoteIfSpecialCharacters handles tabs and newlines', () => { +describe("helpers.svelte", () => { + it("quoteIfSpecialCharacters handles tabs and newlines", () => { // Arrange & Act & Assert - expect(quoteIfSpecialCharacters('foo\tbar')).toBe('"foo\tbar"'); - expect(quoteIfSpecialCharacters('foo\nbar')).toBe('"foo\nbar"'); + expect(quoteIfSpecialCharacters("foo\tbar")).toBe('"foo\tbar"'); + expect(quoteIfSpecialCharacters("foo\nbar")).toBe('"foo\nbar"'); }); - it('quoteIfSpecialCharacters handles empty string and undefined/null', () => { + it("quoteIfSpecialCharacters handles empty string and undefined/null", () => { // Arrange & Act & Assert - expect(quoteIfSpecialCharacters('')).toBe(''); + expect(quoteIfSpecialCharacters("")).toBe(""); expect(quoteIfSpecialCharacters(undefined)).toBeUndefined(); expect(quoteIfSpecialCharacters(null)).toBeNull(); }); - it('quoteIfSpecialCharacters quotes all Lucene special characters', () => { + it("quoteIfSpecialCharacters quotes all Lucene special characters", () => { // Arrange - const luceneSpecials = ['+', '-', '!', '(', ')', '{', '}', '[', ']', '^', '"', '~', '*', '?', ':', '\\', '/']; + const luceneSpecials = [ + "+", + "-", + "!", + "(", + ")", + "{", + "}", + "[", + "]", + "^", + '"', + "~", + "*", + "?", + ":", + "\\", + "/", + ]; // Act & Assert for (const char of luceneSpecials) { @@ -176,50 +195,50 @@ Use accessible queries (not implementation details): ```typescript // ✅ Role-based -screen.getByRole('button', { name: /submit/i }); -screen.getByRole('textbox', { name: /email/i }); +screen.getByRole("button", { name: /submit/i }); +screen.getByRole("textbox", { name: /email/i }); // ✅ Label-based -screen.getByLabelText('Email address'); +screen.getByLabelText("Email address"); // ✅ Text-based -screen.getByText('Welcome back'); +screen.getByText("Welcome back"); // ⚠️ Fallback: Test ID -screen.getByTestId('complex-chart'); +screen.getByTestId("complex-chart"); // ❌ Avoid: Implementation details -screen.getByClassName('btn-primary'); +screen.getByClassName("btn-primary"); ``` ## Mocking Modules ```typescript -import { vi, describe, it, beforeEach, expect } from 'vitest'; -import { render, screen } from '@testing-library/svelte'; +import { vi, describe, it, beforeEach, expect } from "vitest"; +import { render, screen } from "@testing-library/svelte"; -vi.mock('$lib/api/organizations', () => ({ - getOrganizations: vi.fn() +vi.mock("$lib/api/organizations", () => ({ + getOrganizations: vi.fn(), })); -import { getOrganizations } from '$lib/api/organizations'; -import OrganizationList from './organization-list.svelte'; +import { getOrganizations } from "$lib/api/organizations"; +import OrganizationList from "./organization-list.svelte"; -describe('OrganizationList', () => { +describe("OrganizationList", () => { beforeEach(() => { vi.clearAllMocks(); }); - it('displays organizations from API', async () => { + it("displays organizations from API", async () => { // Arrange - const mockOrganizations = [{ id: '1', name: 'Org One' }]; + const mockOrganizations = [{ id: "1", name: "Org One" }]; vi.mocked(getOrganizations).mockResolvedValue(mockOrganizations); // Act render(OrganizationList); // Assert - expect(await screen.findByText('Org One')).toBeInTheDocument(); + expect(await screen.findByText("Org One")).toBeInTheDocument(); }); }); ``` @@ -227,7 +246,7 @@ describe('OrganizationList', () => { ## Snapshot Testing (Use Sparingly) ```typescript -it('matches snapshot', () => { +it("matches snapshot", () => { // Arrange & Act const { container } = render(StaticComponent); diff --git a/.agents/skills/security-principles/SKILL.md b/.agents/skills/security-principles/SKILL.md index 2e56a1e0f..2fbb49b1c 100644 --- a/.agents/skills/security-principles/SKILL.md +++ b/.agents/skills/security-principles/SKILL.md @@ -1,10 +1,10 @@ --- name: security-principles -description: | - Security best practices for the Exceptionless codebase. Secrets management, input validation, - secure defaults, and avoiding common vulnerabilities. - Keywords: security, secrets, encryption, PII, logging, input validation, secure defaults, - environment variables, OWASP, cryptography +description: > + Use this skill when handling secrets, credentials, PII, input validation, or any + security-sensitive code. Covers secrets management, secure defaults, encryption, logging + safety, and common vulnerability prevention. Apply when adding authentication, configuring + environment variables, reviewing code for security issues, or working with sensitive data. --- # Security Principles diff --git a/.agents/skills/shadcn-svelte/SKILL.md b/.agents/skills/shadcn-svelte/SKILL.md index 95cbe8f60..8e7cc0fe6 100644 --- a/.agents/skills/shadcn-svelte/SKILL.md +++ b/.agents/skills/shadcn-svelte/SKILL.md @@ -1,10 +1,10 @@ --- name: shadcn-svelte -description: | - UI components with shadcn-svelte and bits-ui. Component patterns, trigger snippets, - dialog handling, and accessibility. - Keywords: shadcn-svelte, bits-ui, Button, Dialog, Sheet, Popover, DropdownMenu, - Tooltip, Form, Input, Select, child snippet, trigger pattern, cn utility +description: > + Use this skill when building UI with shadcn-svelte or bits-ui components — buttons, dialogs, + sheets, popovers, dropdowns, tooltips, forms, inputs, or selects. Covers import patterns, + trigger snippets, child snippet composition, and the cn utility. Apply when adding or + customizing any shadcn-svelte component in the frontend. --- # shadcn-svelte Components @@ -173,18 +173,18 @@ When using trigger components with custom elements like Button, **always use the ```typescript // options.ts -import type { DropdownItem } from '$shared/types'; +import type { DropdownItem } from "$shared/types"; export enum Status { - Active = 'active', - Inactive = 'inactive', - Pending = 'pending' + Active = "active", + Inactive = "inactive", + Pending = "pending", } export const statusOptions: DropdownItem[] = [ - { value: Status.Active, label: 'Active' }, - { value: Status.Inactive, label: 'Inactive' }, - { value: Status.Pending, label: 'Pending' } + { value: Status.Active, label: "Active" }, + { value: Status.Inactive, label: "Inactive" }, + { value: Status.Pending, label: "Pending" }, ]; ``` diff --git a/.agents/skills/storybook/SKILL.md b/.agents/skills/storybook/SKILL.md index b44ec8689..8b7d786ee 100644 --- a/.agents/skills/storybook/SKILL.md +++ b/.agents/skills/storybook/SKILL.md @@ -1,9 +1,10 @@ --- name: storybook -description: | - Component stories using Storybook with Svelte CSF. Story patterns, defineMeta, argTypes, - snippet-based customization, and visual testing. - Keywords: storybook, stories.svelte, defineMeta, Story, args, argTypes, autodocs +description: > + Use this skill when creating or updating Storybook stories for Svelte components. Covers + Svelte CSF story format, defineMeta, argTypes, snippet-based customization, and autodocs. + Apply when adding visual documentation for components, setting up story files, or running + Storybook for development. --- # Storybook diff --git a/.agents/skills/svelte-components/SKILL.md b/.agents/skills/svelte-components/SKILL.md index b57d36ebd..f838e6901 100644 --- a/.agents/skills/svelte-components/SKILL.md +++ b/.agents/skills/svelte-components/SKILL.md @@ -1,10 +1,10 @@ --- name: svelte-components -description: | - Svelte 5 component patterns for the Exceptionless SPA. Runes, reactivity, props, - events, snippets, component organization, and shadcn-svelte integration. - Keywords: Svelte 5, $state, $derived, $effect, $props, runes, onclick, snippets, - {@render}, reactive, component composition, shadcn-svelte +description: > + Use this skill when writing Svelte 5 components — using runes ($state, $derived, $effect, + $props), handling events, composing with snippets ({@render}), or integrating with + shadcn-svelte. Apply when creating new components, refactoring to Svelte 5 patterns, + or debugging reactivity issues in the frontend. --- # Svelte Components @@ -146,7 +146,7 @@ Use `onclick` instead of `on:click`: ## Snippets (Content Projection) -Replace `` with snippets. From [login/+page.svelte](src/Exceptionless.Web/ClientApp/src/routes/(auth)/login/+page.svelte): +Replace `` with snippets. From [login/+page.svelte](): ```svelte state.errors}> diff --git a/.agents/skills/tanstack-form/SKILL.md b/.agents/skills/tanstack-form/SKILL.md index 831a55d51..d7011192c 100644 --- a/.agents/skills/tanstack-form/SKILL.md +++ b/.agents/skills/tanstack-form/SKILL.md @@ -1,10 +1,10 @@ --- name: tanstack-form -description: | - TanStack Form with Zod validation in Svelte 5. Form state management, field validation, - error handling, and ProblemDetails integration. - Keywords: TanStack Form, createForm, Field, form validation, zod schema, form errors, - onSubmit, onSubmitAsync, problemDetailsToFormErrors +description: > + Use this skill when building or modifying forms with TanStack Form and Zod validation. + Covers createForm, field-level validation, error handling, and mapping ProblemDetails + API errors to form fields. Apply when adding new forms, implementing validation logic, + or handling form submission in the Svelte frontend. --- # TanStack Form @@ -24,23 +24,23 @@ export type LoginFormData = Infer; // Extended in feature schemas.ts // From src/lib/features/auth/schemas.ts -import { ChangePasswordModelSchema } from '$generated/schemas'; +import { ChangePasswordModelSchema } from "$generated/schemas"; export const ChangePasswordSchema = ChangePasswordModelSchema.extend({ - confirm_password: string().min(6).max(100) + confirm_password: string().min(6).max(100), }).refine((data) => data.password === data.confirm_password, { - message: 'Passwords do not match', - path: ['confirm_password'] + message: "Passwords do not match", + path: ["confirm_password"], }); export type ChangePasswordFormData = Infer; // Re-export generated schemas -export { LoginSchema, type LoginFormData } from '$generated/schemas'; +export { LoginSchema, type LoginFormData } from "$generated/schemas"; ``` ## Basic Form Pattern -From [login/+page.svelte](src/Exceptionless.Web/ClientApp/src/routes/(auth)/login/+page.svelte): +From [login/+page.svelte](): ```svelte