mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-08 14:37:04 +02:00
5.8 KiB
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
@/forsrc/directory (configured in Vite & Vitest) - Import Order: External libs → Applesauce → Local
@/modules → Relative imports - Applesauce: Prefer helpers from
applesauce-core/helpersandapplesauce-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-coreandnostr-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
useMemoneeded - Custom Helpers: Use
useMemofor complex transformations - Stable References: Use
useStableValue/useStableArrayfor 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/utilsfor 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.tsfor clean imports - Pure Functions: Business logic in
src/core/logic.ts
Naming Conventions
- Components: PascalCase with descriptive names (
LiveActivityRenderer) - Hooks: camelCase with
useprefix (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.tscontains 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
- Singleton Services: Never create new EventStore, RelayPool, or RelayLiveness instances
- Verification: Always run full verification before committing changes
- Applesauce Caching: Don't use
useMemowith Applesauce helpers - Account Signing: Always check
canSignbefore signing operations - Error Boundaries: Wrap event renderers in error boundaries
- 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.