This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Doughnut is a Personal Knowledge Management (PKM) tool combining zettelkasten-style note capture with spaced-repetition learning features and knowledge sharing capabilities.
This project uses Nix for environment management. With direnv configured, the environment auto-loads when entering the directory.
pnpm sutThis starts all services with auto-reload:
- Backend (Spring Boot with Gradle continuous build)
- Frontend (Vite with HMR)
- Mountebank (mock external services)
DO NOT restart services after code changes - they auto-reload.
Prefix commands with the nix wrapper:
CURSOR_DEV=true nix develop -c <command>| Task | Command |
|---|---|
| Start all services | pnpm sut |
| Run backend tests | pnpm backend:verify |
| Run backend tests (no migration) | pnpm backend:test_only |
| Run frontend tests | pnpm frontend:test |
| Run single frontend test | pnpm -C frontend test tests/path/to/TestFile.spec.ts |
| Run E2E test (single feature) | pnpm cypress run --spec e2e_test/features/path/to.feature |
| Open Cypress IDE | pnpm cy:open |
| Format all code | pnpm format:all |
| Lint all code | pnpm lint:all |
| Regenerate TypeScript from OpenAPI | pnpm generateTypeScript |
| Connect to local DB | mysql -S $MYSQL_HOME/mysql.sock -u doughnut -p (password: doughnut) |
- Backend: Spring Boot 3.x, Java 25, MySQL, Redis, JPA/Hibernate, Flyway migrations
- Frontend: Vue 3, TypeScript, Vite, Pinia, Tailwind CSS, DaisyUI
- Testing: JUnit 5 (backend), Vitest (frontend), Cypress + Cucumber (E2E)
- External Services: OpenAI API, Wikidata API
doughnut/
├── backend/ # Spring Boot backend
│ └── src/main/java/com/odde/doughnut/
│ ├── controllers/ # REST endpoints + DTOs
│ ├── services/ # Business logic, AI integration
│ ├── entities/ # JPA entities + repositories
│ └── configs/ # Spring configurations
├── frontend/ # Vue 3 frontend
│ └── src/
│ ├── pages/ # Page components
│ ├── components/ # Reusable components
│ ├── composables/ # Vue 3 composables
│ ├── store/ # Pinia state management
│ └── (imports from packages/generated/doughnut-backend-api/)
├── packages/
│ └── generated/
│ └── doughnut-backend-api/ # Auto-generated API client
├── e2e_test/ # Cucumber E2E tests
│ ├── features/ # Gherkin feature files
│ ├── step_definitions/ # Step implementations
│ └── start/ # Page objects
└── mcp-server/ # MCP server for Claude integration
- Frontend API client is auto-generated from OpenAPI spec
- After changing backend controller signatures, run
pnpm generateTypeScript - Frontend builds output to
backend/src/main/resources/static/ - Dev server proxies
/api/,/attachments/,/logout/,/testability/to backend
- Test through controllers when possible
- Use
makeMefactory pattern for test data (e.g.,makeMe.aNote().creatorAndOwner(user).please()) - Tests use real database with
@Transactional - Always use proper imports, never inline fully qualified class names
- Use
<script setup lang="ts">for components - Import API services from
@generated/doughnut-backend-api/sdk.gen - Use
apiCallWithLoading()for user-initiated actions with loading/error handling - DaisyUI classes use
daisy-prefix - Avoid
getByRolequeries in tests (performance) - usegetByText,getByLabelText, etc. - Use
mockSdkService()helper for mocking API calls in tests
- Keep step definitions lightweight, delegate to page objects
- Use tags like
@usingMockedOpenAiService,@mockBrowserTimefor test configuration - Backend logs at
backend/logs/doughnut-e2e.log
- Location:
backend/src/main/resources/db/migration/ - Naming:
V{version}__{description}.sql(e.g.,V300000176__description.sql) - Never modify committed migrations; create new ones
- High Cohesion - Minimize duplication, keep related code together
- Keep it Simple - Avoid defensive programming, use minimum code
- No Historical Documentation - Code readers only care about current state
- Test Behavior, Not Implementation - Tests verify pre/post state transitions