Skip to content

Commit 044e5f1

Browse files
committed
Merge main into security fixes
2 parents 0cfa1a2 + a9f6dee commit 044e5f1

18 files changed

Lines changed: 1064 additions & 601 deletions

File tree

.env.example

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,23 @@ MONARCH_API_KEY=your-monarch-api-key
1313
# Leave empty to disable Sentry
1414
SENTRY_DSN=https://275ad5c45f7eb6454f31ae6a8c325f46@o4509941888122880.ingest.us.sentry.io/4509942073131008
1515

16-
# LLM Options (Phase 2 - choose one)
17-
# Option 1: Ollama (FREE, local)
18-
OLLAMA_ENDPOINT=http://localhost:11434
16+
# LLM categorizer — set one of the following keys.
17+
# Auto-detected from whichever key is set. To force a choice when both are
18+
# present, set CATEGORIZER_PROVIDER=openai or CATEGORIZER_PROVIDER=anthropic.
1919

20-
# Option 2: OpenAI (paid)
20+
# OpenAI (default if no provider is forced)
2121
# OPENAI_API_KEY=your-openai-api-key
22+
# OPENAI_MODEL=gpt-5.4-nano
23+
24+
# Anthropic (Claude)
25+
# ANTHROPIC_API_KEY=your-anthropic-api-key # CLAUDE_API_KEY also accepted
26+
# ANTHROPIC_MODEL=claude-haiku-4-5-20251001
2227

23-
# Option 3: Claude API (paid)
24-
# CLAUDE_API_KEY=your-claude-api-key
28+
# Explicit backend selection (optional)
29+
# CATEGORIZER_PROVIDER=anthropic
30+
31+
# Ollama (planned, not yet wired)
32+
OLLAMA_ENDPOINT=http://localhost:11434
2533

2634
# Database (Future)
2735
# DATABASE_URL=postgresql://user:password@localhost/walmart_monarch_sync

.gitignore

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ go.work
3131
.env.local
3232
.env.*.local
3333

34+
# Local auth/session tokens
35+
token.json
36+
*token*.json
37+
*tokens*.json
38+
cookies.json
39+
*cookies*.json
40+
3441
# IDE files
3542
.idea/
3643
*.swp
@@ -69,4 +76,4 @@ temp/
6976
*.sqlite3
7077

7178
# Redis dump
72-
dump.rdb
79+
dump.rdb

AGENTS.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# AGENTS.md
2+
3+
**Last Updated:** June 2026
4+
5+
CLI + API server that syncs Walmart, Costco, and Amazon purchases into Monarch Money:
6+
matches each order to its Monarch transaction, categorizes items with OpenAI, and splits
7+
the transaction by category. Local single-user tool — not a SaaS, extension, or real-time service.
8+
9+
> This file is the single source of truth for agent guidance. `CLAUDE.md` is a symlink to it.
10+
11+
## Commands
12+
13+
```bash
14+
# Build
15+
go build -o itemize ./cmd/itemize/
16+
17+
# Preview a sync — real OpenAI calls, NO Monarch writes. Use this to test safely.
18+
./itemize walmart -dry-run -days 14 -verbose
19+
./itemize costco -dry-run -days 7
20+
21+
# Apply
22+
./itemize walmart -days 14
23+
./itemize costco -days 7 -max 10
24+
./itemize walmart -force # reprocess already-processed orders
25+
26+
# Serve
27+
./itemize serve -port 8085
28+
29+
# Test
30+
go test ./... # all
31+
go test ./... -race # race detector (run before committing)
32+
go test ./... -cover # coverage (keep 80%+)
33+
go test ./internal/domain/categorizer/... -v # single package
34+
```
35+
36+
## Configuration
37+
38+
Reads `config.yaml` or env vars:
39+
- `MONARCH_TOKEN` — Monarch API token (required)
40+
- LLM categorizer — set **one** of:
41+
- `OPENAI_API_KEY` (also `OPENAI_APIKEY`) with optional `OPENAI_MODEL` (default `gpt-5.4-nano`)
42+
- `ANTHROPIC_API_KEY` (also `CLAUDE_API_KEY`) with optional `ANTHROPIC_MODEL` (default `claude-haiku-4-5-20251001`)
43+
- `CATEGORIZER_PROVIDER``openai` or `anthropic` to force a backend when both keys are set (auto-detected otherwise)
44+
- SQLite DB auto-created at `monarch_sync.db`
45+
46+
## Architecture
47+
48+
Layered, with the domain core kept dependency-free. Flow: **CLI → application → domain ← adapters → infrastructure.**
49+
50+
```
51+
internal/
52+
application/sync/ orchestrator + per-provider handlers (walmart, costco, amazon, simple)
53+
domain/ pure logic: categorizer/ (pluggable LLM backend), matcher/ (fuzzy), splitter/ (splits + tax)
54+
adapters/ providers/ (walmart, costco, amazon) and clients/ (Monarch, openai/, anthropic/)
55+
infrastructure/ config/, storage/ (SQLite + goose migrations), logging/ (slog)
56+
cli/ flag parsing + output
57+
```
58+
59+
The categorizer depends on a `ChatClient` interface; concrete LLM backends
60+
live under `internal/adapters/clients/{openai,anthropic}`. Selection happens
61+
in `internal/adapters/clients/clients.go:newChatClient`.
62+
63+
Key entry points:
64+
- Orchestrator — `internal/application/sync/orchestrator.go`
65+
- Matcher (amount ±$0.01, date ±5 days) — `internal/domain/matcher/matcher.go`
66+
- Categorizer (model set here today) — `internal/domain/categorizer/categorizer.go`
67+
- Splitter (tax is proportional: `category_subtotal/total_subtotal * total_tax`) — `internal/domain/splitter/splitter.go`
68+
- Storage / dedup — `internal/infrastructure/storage/sqlite.go`
69+
70+
## Conventions agents must follow
71+
72+
**TDD, and it's mandatory for bug fixes.** Before fixing a bug: write a failing test that
73+
reproduces it, confirm it fails, then fix, then confirm green, then run the full suite, then log
74+
it in `docs/bug-fixes.md`. Don't fix-first.
75+
76+
**Keep the domain layer pure.** No HTTP, DB, file I/O, or clock access in `internal/domain/`
77+
only pure functions over interfaces (`providers.OrderProvider`, `Order`, `OrderItem`). This is
78+
what makes it testable without mocks.
79+
80+
**Log by level, not by `if verbose`.** `-verbose` sets the logger to debug; let the handler filter.
81+
82+
```go
83+
// don't
84+
if opts.Verbose { logger.Info("Processing order", "id", order.ID) }
85+
// do
86+
logger.Debug("Processing order", "id", order.ID)
87+
```
88+
Debug = diagnostics, Info = normal ops, Warn = recoverable, Error = needs attention.
89+
90+
**Never bypass dedup casually.** SQLite tracks processed orders to prevent duplicate splits.
91+
`-force` is the only override and only when intentional — see `docs/deduplication-safety.md`.
92+
93+
**Commits:** `type: brief description` (`feat|fix|refactor|test|docs|chore`). Run `go test ./...`
94+
and a `-dry-run` before committing.
95+
96+
## Adding a provider
97+
98+
Implement `providers.OrderProvider` in `internal/adapters/providers/<name>/`, add a config struct
99+
in `config.go`, register in `internal/cli/providers.go`, add a handler in
100+
`internal/application/sync/handlers/`, and test. Full walkthrough: `docs/adding-providers.md`.
101+
102+
## Gotchas
103+
104+
- **"No matching transaction found"** — transaction not posted yet (wait 1–3 days), amount off by
105+
>$0.01, or date off by >5 days. Use `-verbose` to see match scoring.
106+
- **"Order already processed"** — expected; dedup working. `-force` to reprocess.
107+
- **Provider auth** — Walmart cookies in `~/.walmart-api/cookies.json`; Costco creds saved by costco-go.
108+
- **DB reset** — back up and delete `monarch_sync.db`; migrations (goose) re-run on startup.
109+
110+
## Deeper docs
111+
112+
`docs/architecture.md` · `docs/testing.md` · `docs/adding-providers.md` ·
113+
`docs/deduplication-safety.md` · `docs/bug-fixes.md` · `docs/logging.md` · `docs/design-migrations.md`

0 commit comments

Comments
 (0)