|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Repository layout |
| 6 | + |
| 7 | +| Directory | Purpose | |
| 8 | +|---|---| |
| 9 | +| `timeless-api/` | Java 21 / Quarkus 3 backend + Angular 19 frontend (served via Quinoa) | |
| 10 | +| `whatsapp/` | Node.js 20 service that bridges WhatsApp messages to the API via SQS | |
| 11 | +| `docker/` | Local-dev docker-compose (LocalStack SQS/S3, PostgreSQL, Ollama) | |
| 12 | +| `infrastructure/` | Terraform / AWS provisioning | |
| 13 | +| `site/` | Landing page | |
| 14 | + |
| 15 | +## Commands |
| 16 | + |
| 17 | +### timeless-api (Java/Quarkus) |
| 18 | + |
| 19 | +```bash |
| 20 | +# Dev mode (hot reload) — requires OPENAI_API_KEY in application.properties |
| 21 | +cd timeless-api && ./mvnw quarkus:dev |
| 22 | + |
| 23 | +# Dev mode using local Ollama instead of OpenAI |
| 24 | +cd timeless-api && ./mvnw quarkus:dev -Pollama -Dquarkus.profile=local |
| 25 | + |
| 26 | +# Clean, format, and run all tests (required before submitting a PR) |
| 27 | +cd timeless-api && ./mvnw clean package |
| 28 | + |
| 29 | +# Run a single test class |
| 30 | +cd timeless-api && ./mvnw test -Dtest=SignUpResourceTest |
| 31 | + |
| 32 | +# Run a single test method |
| 33 | +cd timeless-api && ./mvnw test -Dtest=SignUpResourceTest#should_returnCreated_when_validSignUpRequestIsProvided |
| 34 | +``` |
| 35 | + |
| 36 | +### Frontend (Angular — inside Quinoa) |
| 37 | + |
| 38 | +```bash |
| 39 | +cd timeless-api/src/main/webui |
| 40 | +npm run prettier:write # format check + apply |
| 41 | +``` |
| 42 | + |
| 43 | +### whatsapp service |
| 44 | + |
| 45 | +```bash |
| 46 | +cd whatsapp |
| 47 | +npm install |
| 48 | +npm run start:local # loads .env.local |
| 49 | +npm run prettier:write # format check + apply |
| 50 | +``` |
| 51 | + |
| 52 | +### Local infrastructure |
| 53 | + |
| 54 | +```bash |
| 55 | +cd docker && docker-compose up -d # starts LocalStack, PostgreSQL, Ollama |
| 56 | +``` |
| 57 | + |
| 58 | +## Architecture: message processing flow |
| 59 | + |
| 60 | +There are **two parallel paths** for processing WhatsApp messages: |
| 61 | + |
| 62 | +**Synchronous path** (used when the whatsapp service calls the API directly): |
| 63 | +``` |
| 64 | +WhatsApp user → whatsapp/src/index.js → POST /api/messages → MessageResource → LangChain4j AI → DB |
| 65 | +``` |
| 66 | + |
| 67 | +**Async SQS path** (used in production; whatsapp pushes to SQS, API polls): |
| 68 | +``` |
| 69 | +WhatsApp user → whatsapp/src/index.js → SQS (incoming FIFO queue) |
| 70 | + ↓ (polled every 5s) |
| 71 | + timeless-api/SQS.java → LangChain4j AI → DB |
| 72 | + ↓ |
| 73 | + SQS (recognized/processed FIFO queue) |
| 74 | + ↓ |
| 75 | + whatsapp/src/index.js → reply to user |
| 76 | +``` |
| 77 | + |
| 78 | +`SQS.java` (`infra/queue/SQS.java`) is the scheduler that drives the async path. It dispatches to two operation types: `ADD_TRANSACTION` and `GET_BALANCE`. |
| 79 | + |
| 80 | +## AI integration |
| 81 | + |
| 82 | +`TextAiService` and `ImageAiService` are LangChain4j `@RegisterAiService` interfaces. The `TextAiService` prompt (in the annotation) defines the full classification logic — it returns a JSON with `operation` and `content`. The AI must return one of these operations: |
| 83 | + |
| 84 | +- `ADD_TRANSACTION` — extracts amount, description, type (IN/OUT), category |
| 85 | +- `GET_BALANCE` — uses `GetBalanceTool` to query the DB and respond in Portuguese |
| 86 | + |
| 87 | +Categories: `GOALS`, `COMFORT`, `FIXED_COSTS`, `PLEASURES`, `FINANCIAL_FREEDOM`, `KNOWLEDGE` |
| 88 | + |
| 89 | +## Configuration profiles |
| 90 | + |
| 91 | +| Profile | Activated by | Key behavior | |
| 92 | +|---|---|---| |
| 93 | +| `dev` (default) | `quarkus:dev` | Uses OpenAI, requires `OPENAI_API_KEY` in `application.properties` | |
| 94 | +| `local` | `-Dquarkus.profile=local` | Uses Ollama at `localhost:11434`, uses LocalStack for SQS | |
| 95 | +| `test` | `@QuarkusTest` | Disables SQS devservices, disables OpenAI integration, disables scheduler | |
| 96 | + |
| 97 | +## Testing conventions |
| 98 | + |
| 99 | +- Framework: `@QuarkusTest` (RestAssured + real DB, no mocks for persistence) |
| 100 | +- Assertions: AssertJ only (`assertThat(...)`) |
| 101 | +- Test naming: `should_expectedBehavior_when_stateUnderTest` |
| 102 | +- Tests run in integration mode — a real PostgreSQL connection is expected (via Quarkus Dev Services or the test profile config) |
| 103 | + |
| 104 | +## Code style |
| 105 | + |
| 106 | +- Commits: [Conventional Commits](https://www.conventionalcommits.org) format, imperative mood, ≤50 chars on first line |
| 107 | +- Java formatting enforced by `impsort` and `formatter` Maven plugins — `./mvnw clean package` applies them automatically |
| 108 | +- JS/TS formatting enforced by Prettier — run `npm run prettier:write` before committing |
| 109 | + |
| 110 | +## Key env vars |
| 111 | + |
| 112 | +| Variable | Used by | Notes | |
| 113 | +|---|---|---| |
| 114 | +| `OPENAI_API_KEY` | timeless-api, whatsapp | Required for OpenAI mode | |
| 115 | +| `ALLOWED_PHONE_NUMBERS` | whatsapp `.env.local` | Comma-separated list of allowed numbers | |
| 116 | +| `SECURITY_KEY` | timeless-api | AES secret (base64); `%dev` defaults to `YS0xNi1ieXRlLXNlY3JldA==` | |
| 117 | +| `JWT_PUBLIC_KEY` / `JWT_PRIVATE_KEY` | timeless-api | SmallRye JWT signing keys | |
| 118 | +| `INCOMING_MESSAGE_FIFO_URL` / `RECOGNIZED_MESSAGE_FIFO_URL` | both services | SQS FIFO queue URLs | |
0 commit comments