Files
grimoire/AGENTS.md
Alejandro Gómez 0ebe1ec3da ai: agents file
2026-01-22 18:38:26 +01:00

5.8 KiB

AGENTS.md

This file contains guidelines for agentic coding agents working in the Grimoire repository.

Quick Reference

For detailed project architecture, patterns, and conventions, see CLAUDE.md.

Build Commands

Development

npm run dev          # Start development server with HMR
npm run build        # TypeScript check + production build
npm run preview      # Preview production build locally

Code Quality

npm run lint         # Run ESLint (fails on errors)
npm run lint:fix     # Auto-fix ESLint issues + formatting
npm run format       # Format with Prettier
npm run format:check # Check formatting without changes

Testing

npm test             # Run tests in watch mode
npm run test:ui      # Visual test explorer
npm run test:run     # Single test run (CI mode)

Single Test: Use vitest run with file pattern:

npm run test:run src/lib/nostr-utils.test.ts

Verification

Always run before committing:

npm run lint && npm run test:run && npm run build

Code Style Guidelines

Imports & Dependencies

  • Path Alias: Use @/ for src/ directory (configured in Vite & Vitest)
  • Import Order: External libs → Applesauce → Local @/ modules → Relative imports
  • Applesauce: Prefer helpers from applesauce-core/helpers and applesauce-common/helpers
  • No Default Exports: Use named exports for better tree-shaking
// ✅ Correct import order
import { useState } from "react";
import { use$ } from "applesauce-react/hooks";
import { getProfileContent } from "applesauce-core/helpers";
import { cn } from "@/lib/utils";
import { UserName } from "./components/UserName";

TypeScript & Types

  • Strict Mode: Project uses strict TypeScript configuration
  • No any: ESLint rule disabled, but prefer proper types
  • Applesauce Types: Use types from applesauce-core and nostr-tools
  • Local Types: Extend in src/types/ when needed
// ✅ Use proper types from Applesauce
import type { NostrEvent, ProfileContent } from "applesauce-core/helpers";
import type { EventPointer } from "nostr-tools/nip19";

React Patterns

  • React 19: Use latest features (hooks, concurrent rendering)
  • No Default Exports: Components use named exports
  • Props Interface: Always define props interface with JSDoc comments
  • Destructured Props: Destructure props in function signature
// ✅ Component pattern
interface QuotedEventProps {
  /** EventPointer with optional relay hints */
  eventPointer?: EventPointer;
  /** Depth level for nesting */
  depth?: number;
}

export function QuotedEvent({ eventPointer, depth = 0 }: QuotedEventProps) {
  // Component logic
}

Performance & Caching

  • Applesauce Helpers: Cache internally - NO useMemo needed
  • Custom Helpers: Use useMemo for complex transformations
  • Stable References: Use useStableValue/useStableArray for filters/options
// ❌ WRONG - Unnecessary memoization
const title = useMemo(() => getArticleTitle(event), [event]);

// ✅ CORRECT - Helpers cache internally
const title = getArticleTitle(event);

Styling

  • TailwindCSS: Primary styling approach
  • CSS Variables: Theme tokens in HSL format (see index.css)
  • Utility Function: Use cn() from @/lib/utils for class merging
  • Dark Mode: Default (controlled via HTML class)
// ✅ Styling pattern
import { cn } from "@/lib/utils";

const className = cn(
  "flex items-center gap-2 p-2 rounded-lg",
  isActive && "bg-primary text-primary-foreground",
  className
);

File Organization

  • By Domain: Group files by feature/domain (nostr/, ui/, services/, hooks/)
  • Colocated Tests: Test files next to source files (*.test.ts)
  • Barrel Exports: Use index.ts for clean imports
  • Pure Functions: Business logic in src/core/logic.ts

Naming Conventions

  • Components: PascalCase with descriptive names (LiveActivityRenderer)
  • Hooks: camelCase with use prefix (useAccount, useProfile)
  • Utilities: camelCase with descriptive names (getDisplayName, canAccountSign)
  • Constants: UPPER_SNAKE_CASE for exports
  • Files: kebab-case for utilities, PascalCase for components

Testing Guidelines

Test Environment

  • Vitest: Test framework with Node environment
  • Polyfills: IndexedDB, WebSocket, localStorage pre-configured
  • Setup File: src/test/setup.ts contains browser API polyfills

What to Test

  • Parsers: All argument parsing logic and edge cases
  • Pure Functions: State mutations and business logic
  • Utilities: Helper functions and data transformations
  • Not UI Components: React components tested manually

Test Structure

describe("parseReqCommand", () => {
  describe("kind flag (-k, --kind)", () => {
    it("should parse single kind", () => {
      const result = parseReqCommand(["-k", "1"]);
      expect(result.filter.kinds).toEqual([1]);
    });
  });
});

Critical Rules

  1. Singleton Services: Never create new EventStore, RelayPool, or RelayLiveness instances
  2. Verification: Always run full verification before committing changes
  3. Applesauce Caching: Don't use useMemo with Applesauce helpers
  4. Account Signing: Always check canSign before signing operations
  5. Error Boundaries: Wrap event renderers in error boundaries
  6. Path Alias: Use @/ for all internal imports

Project Context

Grimoire is a Nostr protocol explorer with a tiling window manager interface. Each window is a Nostr "app" (profile viewer, event feed, NIP documentation, etc.). Commands are launched Unix-style via Cmd+K palette.

Stack: React 19 + TypeScript + Vite + TailwindCSS + Jotai + Dexie + Applesauce


See CLAUDE.md for detailed architecture, patterns, hooks, and conventions.