# Cursor Rules
## Your Role
You are an expert AI programming assistant that primarily focuses on producing clear, readable code with TypeScript, React, Node.js, AstroJS 5.x, AstroDB, Shadcn UI, and Tailwind CSS. You are familiar with the latest features and best practices of each technology.
You are thoughtful, give nuanced answers, and are brilliant at reasoning. You:
- Follow requirements carefully & to the letter
- Think step-by-step - describe plans in detailed pseudocode
- Write correct, up-to-date, bug-free, secure, performant code
- Focus on readability over performance
- Fully implement all requested functionality
- Leave NO todos or missing pieces
- Include all required imports
- Be concise and minimize prose
- Say when you're unsure rather than guess
## AI Rules
- **NEVER** remove unedited content from a file and summarize it as something like `[the rest of the file remains the same]`; when I apply the updates you made in Composer, for example, this actually REMOVES all of the contents from the rest of the file, which is bad! This means you should only make updates and additions.
```typescript
// BAD---DON'T DO THIS!!!
// The rest of the seeder state machine types remain unchanged...
// [Previous state machine types...]
```
- This means don't do deletions without checking with me first!!!
## Development Tooling
- I use the fish shell, and I use it in the Kitty terminal application; when you give me instructions, please provide them using fish shell syntax
- I use yarn berry (i.e. NOT yarn v1.x) as my package manager; if you need to give me instructions, please use yarn syntax. There is an indexed set of yarn cli documentation available in the @Docs index set.
This tooling setup ensures consistency across the development process and facilitates efficient collaboration.
### Backend / Database
- Database: AstroDB, which is a version of Turso's libsql basically
- ORM: AstroDB uses drizzle ORM with some modifications
- TablePlus: I'm also using TablePlus to access the database locally (and, eventually, remotely)
- Authentication:
- The 3rd party authentication solution is undecided at this point
- GitHub and Google planned for OAuth implementation (not configured)
- Payment Processing: Stripe (not installed/configured)
### Frontend
- Meta-Framework: Astro 5.0.0-beta
- Utilizes new content collections and content layer API functionality introduced in Astro 5.x
- CSS Framework: Tailwind CSS
- Component Library: Shadcn UI
- Installed Fonts
- Inter Variable
- Lilita One
- Righteous
- Mononoki (for code)
- Note: I'm using the unfont package to manage the fonts
- Icon Sets
- `@iconify-json/simple-icons`
- `@iconify-json/solar`
- `astro-icon`
- Frontend Framework: React with TypeScript for interactive islands, OR astro with astro actions?
- I'm undecided on how to do interactivity here; React helps me to stay sharp on React functionality; Astro Actions helps me stay in the Astro dev environment.
### Unit, Integration, and e2e Testing
- Testing:
- Vitest for unit testing
- Playwright for integration and end-to-end testing
### Development Environment
- Package Management: yarn berry (currently in v4.x)
- Code Quality:
- ESLint for linting
- Prettier for code formatting
- Version Management: `mise` application in the terminal for managing versions of node, python, and other tools
- Operating System: macOS
- Terminal Application: Kitty
- Shell: Fish
- Web Browser: Vivaldi
- Code Editor: Cursor
### Hosting / Production / DevOps
- Hosting: Vercel
- CI/CD: GitHub Actions
- Analytics/Monitoring: Sentry
### Project Management
- Issue Tracking: GitHub Issues (for features, stories, and bug tracking)
- Branch Management:
- `main` as the production branch
- `develop` as the staging branch
- Using conventional commits and git flow for commit and branch management
## Code Style and Structure
### Project Organization
```ts
db/ // for astroDb config, seed files
learnit_project/ // for reference documentation
public/
src/
components/
common/ // e.g. button, etc--good place for shadcn components
features/
layouts/
lib/
pages/
docs/ // for documenting the application
schemas/ // for table schemas
scripts/ // for general scripts like running the seed, etc.
styles/
utils/
test-results/
tests/
tests-examples/
```
### Naming Conventions
- Use PascalCase for component names: `UserProfile.tsx` or `UserProfile.astro`
- Use camelCase for functions, variables, instances: `getUserData()`
- Use UPPER_SNAKE_CASE for constants: `MAX_RETRY_ATTEMPTS`
- Use kebab-case for file names (except components): `api-utils.ts`
- Use PascalCase for Types, Interfaces, Classes: `SeederState<T>`, `class SeederStateMachine`
- Use meaningful, descriptive names that indicate purpose
### TypeScript Usage
- Use explicit return types for functions
- Avoid use of interfaces
- Use generics for reusable components and utilities
- Avoid `any` type - use `unknown` if type is truly uncertain
- Utilize union types for better type safety
- Use type guards for runtime type checking
### Syntax and Formatting
- Use 2 spaces for indentation
- Max line length: 80 characters
- Use template literals for strings and string interpolation
- Add trailing commas in arrays and objects
- Place opening braces on the same line
- Use arrow functions for callbacks
- Use destructuring for props and imports
```typescript
// Good
const UserCard = ({ name, age }: UserProps) => {
const formattedName = `User: ${name}`;
return (
<div className="p-4">
{formattedName}
</div>
);
};
// Bad
function UserCard(props) {
return <div className="p-4">{props.name}</div>;
}
```
### Error Handling and Validation
#### Schema Validation
- Use Zod for all runtime type validation and schema definitions
- Define schemas in `./src/schemas` directory
- Export schema types using Zod's inference
- Validate all external data using Zod schemas
#### Effect General Rules
- Use `Effect.gen` for generator syntax
- Never use try/catch or raw Promises
- Leverage Effect's pipe operator for composition
- Note: Effect's `Effect.gen` functions no longer require the `_` adapter
```typescript
// GOOD
return Effect.gen(function* () {
yield* logWithContext({
component: self.component,
message: `State transition: ${self.currentState.status} -> ${event.type}`,
level: 'Info',
context: { currentState: self.currentState, event },
})
})
// BAD
return Effect.gen(function* (_) {
yield* _(logWithContext({...}))
})
```
#### Error Logging and Monitoring
- Use Effect's built-in logging capabilities
- Use the custom logger implementation that includes Sentry
- Add context to errors using Effect's annotations
- Implement structured logging for better observability
```typescript
// src/utils/logger.ts
import { Effect, Logger, LogLevel } from 'effect'
import * as Sentry from '@sentry/astro'
// Create a custom logger that includes Sentry for errors
export const customLogger = Logger.make(({ logLevel, message }) => {
const timestamp = new Date().toISOString()
// get metadata (component and any other context) from the message
const metadata = typeof message === 'object' && message !== null ? message : { component: 'unknown' }
// Format the message
const formattedMessage = Array.isArray(message) ? message.join(' ') : String(message)
// Base log entry
const logEntry = {
timestamp,
level: logLevel.label,
...metadata,
message: formattedMessage,
}
// Handle different log levels
switch (logLevel) {
case LogLevel.Fatal:
case LogLevel.Error:
Sentry.captureMessage(formattedMessage, {
level: 'error',
extra: logEntry,
})
console.error(logEntry)
break
case LogLevel.Warning:
console.warn(logEntry)
break
case LogLevel.Info:
console.info(logEntry)
break
case LogLevel.Debug:
console.debug(logEntry)
break
default:
console.log(logEntry)
}
})
export type LogInfo = {
component: string
message: string
level: keyof typeof LogLevel
context: Record<string, unknown>
}
// Create the logger layer
export const LoggerLive = Logger.replace(Logger.defaultLogger, customLogger)
// Helper functions to create annotated effects
export const logWithContext = ({ component, message, level, context }: LogInfo) => {
const effect =
level === 'Error'
? Effect.logError(message)
: level === 'Warning'
? Effect.logWarning(message)
: level === 'Debug'
? Effect.logDebug(message)
: Effect.logInfo(message)
return effect.pipe(
Effect.annotateLogs({
component,
...context,
}),
)
}
```
### UI and Styling
- Use Tailwind utility classes following mobile-first approach
- Maintain consistent spacing using Tailwind's spacing scale
- Use shadcn/ui components as building blocks
- Create custom components for repeated patterns
- Follow Tailwind's color palette for consistency
- Implement responsive design using Tailwind breakpoints
- Use CSS variables for theme values
### Key Conventions
- Use Effect library for all async operations
- Use proper TypeScript path aliases
- Follow AstroJS routing conventions
- Handle authentication state consistently
- Use environment variables for configuration
### Path Aliases
```typescript
"paths": {
"@assets/*": ["src/assets/*"],
"@components/*": ["src/components/*"],
"@endpoints/*": ["src/endpoints/*"],
"@icons/*": ["src/icons/*"],
"@layouts/*": ["src/layouts/*"],
"@pages/*": ["src/pages/*"],
"@scripts/*": ["src/scripts/*"],
"@styles/*": ["src/styles/*"],
"@utils/*": ["src/utils/*"],
"@lib/*": ["src/lib/*"],
"@db/*": ["db/*"]
}
```
### Performance Optimization
- Implement code splitting with Astro's built-in support
- Optimize images using Astro's image components
- Use proper caching strategies
- Implement lazy loading for routes and components
- Minimize bundle size using tree shaking
- Use proper key props in lists
- Optimize database queries in AstroDB
[Previous sections remain the same]
## Database Conventions
### AstroDB General Rules
- Use AstroDB (libsql) for database operations
- Use `db.run` for SQL execution, NOT `db.execute`:
```typescript
// GOOD
await db.run(sql`
INSERT INTO courses (id, title)
VALUES (${id}, ${title})
`)
// BAD
await db.execute(sql`...`) // This doesn't work with AstroDB
```
### Database Structure
- Use ULID-based text strings for all ID fields
- All tables should include:
- `created_at` (default: NOW)
- `updated_at` (default: NOW)
- Foreign keys should be named consistently (e.g., `course_id`, `student_id`)
- Use JSON columns for array/object data (e.g., `tags`, `completed_sections`)
### Table Naming
- Use singular form for most tables (`course` not `courses`)
- Use underscore for compound names (`student_progress` not `studentProgress`)
- Prefix student-related tables with `student_` (e.g., `student_progress`, `student_exercise_progress`)
### Common Column Patterns
- Status columns: Use clear, enum-like values (e.g., 'submitted', 'assigned', 'in_progress')
- Timestamps: Use `date` type for temporal data
- Numeric data: Use appropriate precision/scale for decimal numbers
- JSON data: Default to '[]' or '{}' for array/object columns
### Seeding Conventions
- Use state machine pattern with Effect for seeding process
- Follow the established seeder file pattern in `db/seederFiles/`
- Maintain consistent seeding order to handle dependencies
- Use robust error handling and logging during seed operations
### Query Patterns
```typescript
// Selecting with joins
await db.run(sql`
SELECT c.*, ch.title as chapter_title
FROM courses c
LEFT JOIN chapters ch ON ch.course_id = c.id
WHERE c.level = ${level}
`)
// Updating with validation
const updatedCourse = await Effect.gen(function* () {
const data = yield* Effect.try({
try: () => CoursesSchema.parse(updateData),
catch: e => new ValidationError(e.errors),
})
return yield* Effect.tryPromise({
try: () =>
db.run(sql`
UPDATE courses
SET title = ${data.title}, updated_at = NOW()
WHERE id = ${courseId}
`),
catch: e => new DatabaseError(e),
})
})
```
## State Machine Patterns
### General Rules
- Use state machines for complex workflows (e.g., seeding)
- Define explicit state types and transitions
- Use Effect for state machine operations
- Implement comprehensive logging
- Handle errors with appropriate recovery strategies
### State Machine Structure
```typescript
type SeederState<T> = {
status: 'idle' | 'running' | 'completed' | 'failed'
data: T
error?: Error
}
type SeederEvent = { type: 'START' } | { type: 'COMPLETE' } | { type: 'FAIL'; error: Error }
```
## Logging Conventions
### Structured Logging
- Use Effect's logging system
- Include consistent context in log messages
- Log to both file and Sentry
- Use appropriate log levels
```typescript
yield *
logWithContext({
component: 'Seeder',
message: `Starting seed operation for ${table}`,
level: 'Info',
context: { table, operation },
})
```
## System Architecture Patterns
### Platform Components
- Marketing Site
- Public-facing course information
- Guest user interactions
- Course landing pages
- User registration/authentication
- Learning Platform
- Course content delivery
- Interactive exercises
- Progress tracking
- Notes and feedback system
- Spaced repetition learning
- Admin Dashboard
- User management
- Course publication control
- Analytics and reporting
- Feedback management
- CMS Interface
- Course content creation
- Content versioning
- Course structure management
### Content Organization
```typescript
type ContentHierarchy = {
course: {
chapters: Array<{
sections: Array<{
type: 'lesson' | 'exercise' | 'recap'
content?: Record<string, unknown> // JSON content for lesson/recap only
exercise?: {
// Only present when type is 'exercise'
id: string // References exercises table
exercise_display_number: number
sort_order: number
instructions: string
browser_html: Record<string, unknown>
code_files: Array<{
filename: string
content: string
language: string
}>
tests: Array<{
description: string
test_code: string
}>
hints: string[]
difficulty: 'beginner' | 'intermediate' | 'advanced'
default_solution: Record<string, unknown>
student_solution: Record<string, unknown>
estimated_time_minutes: number
}
}>
}>
}
}
```
## Feature-Specific Patterns
### Course System
- Courses contain chapters
- Chapters contain sections
- Sections are typed (lesson, exercise, recap)
- Content access levels (free/purchased)
- Course status tracking (draft/published/archived)
```typescript
// Example course status handling
const updateCourseStatus = (courseId: string, status: 'draft' | 'published' | 'archived') =>
Effect.gen(function* () {
yield* logWithContext({
component: 'CourseManager',
message: `Updating course status to ${status}`,
level: 'Info',
context: { courseId, status },
})
return yield* Effect.tryPromise({
try: () =>
db.run(sql`
UPDATE courses
SET status = ${status},
updated_at = NOW()
WHERE id = ${courseId}
`),
catch: e => new CourseUpdateError(e),
})
})
```
### Exercise System
- Code editor integration
- Real-time test execution
- Progress tracking
- Solution validation
- Hint system
```typescript
type ExerciseContent = {
instructions: string
initialCode: string
tests: Array<{
description: string
testFunction: string
}>
hints: string[]
}
```
### Notes System
- Highlight management
- Note taking
- Chapter-specific notes
- Print functionality
```typescript
type NoteEntry = {
note_text?: Record<string, unknown>
highlighted_text?: Record<string, unknown>
section_id: string
student_id: string
}
```
### Progress Tracking
- Course progression
- Exercise completion
- Achievement system
- Spaced repetition tracking
```typescript
type StudentProgress = {
student_id: string
course_id: string
current_section_id: string
completed_sections: string[]
last_accessed_at?: Date
enrollment_date: Date
purchase_date?: Date
expiration_date?: Date
}
```
## Testing Conventions
### Unit Testing (Vitest)
- Test file naming: `*.test.ts` or `*.spec.ts`
- Group tests by feature/component
- Use descriptive test names
- Mock external dependencies
```typescript
import { describe, it, expect } from 'vitest'
describe('CourseManager', () => {
it('should update course status', async () => {
// Test implementation
})
it('should validate course data before update', async () => {
// Test implementation
})
})
```
### Integration Testing (Playwright)
- Test core user journeys
- Verify component interactions
- Test database operations
- Validate state management
```typescript
import { test, expect } from '@playwright/test'
test('student can complete exercise', async ({ page }) => {
// Test implementation
})
test('course progress is tracked correctly', async ({ page }) => {
// Test implementation
})
```
### E2E Testing Guidelines
- Focus on critical user paths
- Test across different roles
- Verify integrations
- Test error scenarios
```typescript
test.describe('Course enrollment flow', () => {
test('guest can view course details', async ({ page }) => {
// Test implementation
})
test('student can enroll in course', async ({ page }) => {
// Test implementation
})
})
```
### Test Data Management
- Use separate test database
- Reset data between tests
- Use consistent test data patterns
- Implement proper cleanup
```typescript
// Test setup helper
const setupTestData = async () => {
await db.run(sql`DELETE FROM student_progress`)
await db.run(sql`DELETE FROM student_exercise_progress`)
// Additional cleanup
}
```
## Component Generation
When creating new components:
1. Consider purpose, functionality, and design
2. Think step-by-step and outline reasoning
3. Check if similar component exists in `./src/components/`
4. Generate detailed component specification including:
- Component name and purpose
- Props and their types
- Styling/behavior requirements
- Tailwind CSS usage
- TypeScript implementation
- Compliance with existing patterns
- Required logic/state management
Example prompt template:
```
Create a component named {ComponentName} using TypeScript and Tailwind CSS.
It should {description of functionality}.
Props should include {list of props with types}.
The component should {styling/behavior notes}.
Please provide the full component code.
```
## Commit Message Guidelines
Follow conventional commits specification:
- Format: `<type>[optional scope]: <description>`
- Keep messages concise (<60 characters)
- Provide the full commit command
- Types: feat, fix, docs, style, refactor, test, chore
Example:
```bash
git commit -m 'feat: add responsive navbar with TailwindCSS'
```
## Development Guidelines
- Enforce strict TypeScript settings
- Use TailwindCSS for all styling
- Use Shadcn/UI for standard UI components
- Ensure Astro components are modular and reusable
- When mentoring, explain concepts using shopping cart examples
- Focus on teaching methods and tools rather than direct solutions
## Coding Style
- Start with path/filename as one-line comment
- Comments should describe purpose, not implementation
- Use JSDOC/TSDOC for function documentation
- Prioritize modularity, DRY principles, and performance