# AGENTS.md - AI Coding Agent Guidelines Guidelines for AI coding agents working on BillAI - a microservices bill analysis system. **Version:** See `CHANGELOG.md` for current version. Latest tag usually matches. ## Architecture - `web/` - SvelteKit 5 + TailwindCSS 4 + TypeScript (Frontend, port 3000) - `server/` - Go 1.24 + Gin + MongoDB (API, port 8080) - `analyzer/` - Python 3.12 + FastAPI (Data cleaning, port 8001) **Proxy Caveat:** SvelteKit proxies `/api/*` to Go via `web/src/routes/api/[...path]/+server.ts`, but **only GET and POST are forwarded**. The Go backend intentionally uses `POST` for mutations (update, delete, manual create) to work around this. If you add PUT/PATCH/DELETE endpoints, you must also add them to the proxy. ## Build/Lint/Test Commands ### Frontend (`web/`) ```bash npm run dev # Start dev server npm run build # Production build npm run check # TypeScript + Svelte check npm run lint # Prettier --check + ESLint npm run format # Prettier --write npm run test # vitest --run (CI mode) npx vitest run src/xxx.spec.ts # Run single test file ``` **Note:** `web/` has a `yarn.lock` but scripts in `package.json` use `npm run`. ### Backend (`server/`) ```bash go run . # Start server (reads server/config.yaml) go build -o server . go test ./... # Run all tests go test -run TestName ./... # Run single test go test -v ./handler/... # Verbose output ``` ### Analyzer (`analyzer/`) ```bash python server.py # Start FastAPI (has `if __name__ == "__main__"`) uvicorn server:app --reload # Requires cwd == analyzer/ pytest # Run all tests pytest test_jd_cleaner.py # Single test file ``` ### Docker ```bash docker compose up -d --build --remove-orphans # Start/rebuild all docker compose logs -f server # Follow logs docker compose down # Stop services ``` ## Code Style & Conventions ### TypeScript/Svelte (`web/`) - **Formatting:** Prettier (tabs, single quotes, `trailingComma: none`, printWidth 100) - **Imports:** Use `$lib` alias. No relative imports for lib modules. - **Svelte 5:** Runes (`$state`, `$derived`, `$effect`, `$props`). Events: `onclick={fn}`. - **Types:** `export interface` for models. Frontend uses `camelCase`, API uses `snake_case`. Converters live in `$lib/models/bill.ts`. - **Auth:** Token stored in `localStorage` key `auth`. Always use `apiFetch()` from `$lib/api.ts` for authenticated requests. ### Go (`server/`) - **Module:** `billai-server` (import path). Use this in `go test` / `go build` when outside the directory. - **Layer:** `handler` → `service` → `adapter`/`repository` → `model`. No business logic in handlers. - **Struct tags:** JSON `snake_case`, `omitempty` for optional. Pointer types for optional patch fields. Sensitive fields: `json:"-"`. - **Response shapes:** - Business APIs: `result bool`, `message string`, `data *T` - Auth APIs: `success bool`, `error string`, `data *T` (and `code` for error types) - **Time:** Custom `LocalTime` type serializes as `"2006-01-02 15:04:05"`. - **Soft delete:** Never hard-delete. All queries filter `is_deleted: false`. ### Python (`analyzer/`) - **Style:** PEP 8. `snake_case` vars, `UPPER_CASE` constants. Prefix private globals with `_`. - **Type hints:** Mandatory. Prefer `str | None` or `Optional[str]`. - **Models:** `pydantic.BaseModel` for API schemas. - **Cleaners:** Extend `BaseCleaner(ABC)` from `cleaners/base.py`. Category rules in `config/category.yaml`. ## Key Patterns & Quirks ### API Flow ``` Browser → SvelteKit proxy → Go (Gin) → handler → service → adapter → Python FastAPI └→ repository → MongoDB ``` ### Authentication - JWT (HS256). Header: `Authorization: Bearer `. - `middleware.AuthRequired()` guards authed routes. Public routes: `/api/auth/*`, `/api/changelog`, `/health`. - Frontend `apiFetch()` intercepts 401 → `auth.logout()` + redirect `/login`. ### File Processing Pipeline Upload: ZIP/XLSX → Extract → Convert UTF-8 CSV → Detect bill type (alipay/wechat/jd) → Deduplicate against MongoDB → Clean via Python → Save cleaned data. ### Adapter (Go ↔ Python) `adapter.Cleaner` interface. Two modes: - `http` (default): calls FastAPI at `ANALYZER_URL` - `subprocess`: spawns `python analyzer/clean_bill.py` Set via `ANALYZER_MODE` env var or `server/config.yaml` `analyzer.mode`. ### Config Precedence Go backend reads `server/config.yaml`, but Docker compose sets env vars (`ANALYZER_URL`, `MONGO_URI`, `JWT_SECRET`, etc.) that override it. ### SvelteKit Config Notes - `svelte.config.js` uses `adapter-node` for Docker SSR. - `csrf.trustedOrigins: ['*']` disables CSRF checks. - `onwarn` ignores all `a11y_*` warnings (chart components). ### Deployment - Gitea Actions self-hosted runner (`.gitea/workflows/deploy.yaml`), not GitHub. - `deploy.sh` is the manual deployment script (same logic as CI). ### Test Coverage Sparse. Existing tests: - `web/src/demo.spec.ts` / `page.svelte.spec.ts` - `server/service/changelog_test.go` - `analyzer/test_jd_cleaner.py` ## Important Files | File | Role | |---|---| | `web/src/lib/api.ts` | Central API client, auth injection, all API functions | | `web/src/lib/stores/auth.ts` | Auth state, JWT handling, localStorage key `auth` | | `web/src/lib/models/bill.ts` | UIBill model + snake_case ↔ camelCase converters | | `web/src/routes/api/[...path]/+server.ts` | SvelteKit → Go proxy (GET/POST only) | | `server/main.go` | Entry point, wires adapter + repository + router | | `server/config.yaml` | Go backend config (Mongo, auth, paths, analyzer mode) | | `server/router/router.go` | Route table, auth group definitions | | `server/handler/upload.go` | Full upload pipeline handler | | `server/handler/bills.go` | List/filter/update/delete bills | | `server/model/bill.go` | Bill models, LocalTime type, BSON/JSON marshaling | | `server/adapter/adapter.go` | Cleaner interface definition | | `server/repository/mongo/repository.go` | MongoDB implementation, soft-delete queries | | `analyzer/server.py` | FastAPI entry, bill detection/clean endpoints | | `analyzer/cleaners/base.py` | BaseCleaner ABC | | `analyzer/category.py` | Category inference engine | | `docker-compose.yaml` | Full stack orchestration | ## Agent Guidelines - **Before coding:** Search codebase to understand existing patterns and dependencies. - **Dependencies:** Check `package.json`/`go.mod`/`requirements.txt` before adding new packages. - **Tests:** Run the relevant test suite before committing. If no tests exist for your change, verify manually. - **Git commits:** Provide clear messages explaining the "why" of changes. - **File references:** Use relative `file_path:line_number` format (e.g., `server/handler/changelog.go:12`) when mentioning code locations.