fleece-cli 0.2.0__tar.gz → 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. fleece_cli-0.3.1/.agents/skills/fleece/SKILL.md +186 -0
  2. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/.claude/skills/fleece-compare.md +12 -0
  3. fleece_cli-0.3.1/.claude/skills/fleece-flights.md +51 -0
  4. fleece_cli-0.3.1/.claude/skills/fleece-hotels.md +48 -0
  5. fleece_cli-0.3.1/.claude/skills/fleece-mcc.md +65 -0
  6. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/.claude/skills/fleece-rates.md +10 -0
  7. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/.claude/skills/fleece-recommend.md +16 -0
  8. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/.claude/skills/fleece-wallet.md +17 -0
  9. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/.github/workflows/publish.yml +3 -3
  10. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/PKG-INFO +1 -1
  11. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/cli.py +98 -9
  12. fleece_cli-0.3.1/docs/SEO.md +113 -0
  13. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/docs/index.html +130 -8
  14. fleece_cli-0.3.1/docs/robots.txt +4 -0
  15. fleece_cli-0.3.1/docs/sitemap.xml +9 -0
  16. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/install.sh +5 -3
  17. fleece_cli-0.3.1/mcc_codes.jsonl +981 -0
  18. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/pyproject.toml +2 -2
  19. fleece_cli-0.2.0/.agents/skills/fleece/SKILL.md +0 -115
  20. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/.claude/skills/fleece-card.md +0 -0
  21. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/.claude/skills/fleece-credits.md +0 -0
  22. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/.claude/skills/fleece-news.md +0 -0
  23. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/.claude/skills/fleece-partners.md +0 -0
  24. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/.claude/skills/fleece-roi.md +0 -0
  25. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/.devcontainer/devcontainer.json +0 -0
  26. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/.github/workflows/publish-skills.yml +0 -0
  27. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/.gitignore +0 -0
  28. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/.streamlit/config.toml +0 -0
  29. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/Advertiser Disclosure.md +0 -0
  30. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/CLAUDE.md +0 -0
  31. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/KnowledgeCatalog.json +0 -0
  32. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/LICENSE +0 -0
  33. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/MyCards.json +0 -0
  34. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/README.md +0 -0
  35. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/app.yaml +0 -0
  36. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/assets/default_card.png +0 -0
  37. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/assets/default_card.svg +0 -0
  38. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/db.py +0 -0
  39. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/docs/CNAME +0 -0
  40. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/fleece poc.ipynb +0 -0
  41. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/fleece.py +0 -0
  42. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/image_service.py +0 -0
  43. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/migrate.py +0 -0
  44. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/pages/credit_cards.py +0 -0
  45. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/pages/my_credit_cards.py +0 -0
  46. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/plan.md +0 -0
  47. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/pointsyeah.py +0 -0
  48. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/prompts/agent_system_prompt.py +0 -0
  49. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/reference/Analyzer.md +0 -0
  50. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/requirements-test.txt +0 -0
  51. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/requirements.txt +0 -0
  52. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/style.css +0 -0
  53. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/test_fleece.py +0 -0
  54. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/tests/__init__.py +0 -0
  55. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/tests/test_brave_client.py +0 -0
  56. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/tests/test_cli.py +0 -0
  57. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/tests/test_pointsyeah.py +0 -0
  58. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/tests/test_tools.py +0 -0
  59. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/tools/__init__.py +0 -0
  60. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/tools/brave_client.py +0 -0
  61. {fleece_cli-0.2.0 → fleece_cli-0.3.1}/tools/credit_card_tools.py +0 -0
@@ -0,0 +1,186 @@
1
+ ---
2
+ name: fleece
3
+ description: "What credit card should I use for dining, travel, groceries, or gas?" — Fleece answers this with live data. Looks up rewards rates, annual fees, welcome bonuses, statement credits, and transfer partners for Chase, Amex, Citi, Capital One, Bilt, and all major US issuers. Compare cards side-by-side, find gaps in your wallet, estimate first-year ROI, get personalized card recommendations, and generate PointsYeah award flight and hotel search URLs. Install: `pip install fleece-cli`.
4
+ ---
5
+
6
+ # Fleece — Credit Card Research & Redemption
7
+
8
+ Use this skill when the user asks about:
9
+ - **Specific cards**: "What are the Amex Gold benefits?", "Is the Chase Sapphire Preferred worth it?", "What changed with the Citi Double Cash?"
10
+ - **Earning rates**: "Which card earns the most on dining?", "What's the best card for groceries?"
11
+ - **Transfer partners**: "Where can I transfer Chase Ultimate Rewards?", "What airlines does Amex transfer to?"
12
+ - **Statement credits**: "How do I use the Amex Gold dining credit?", "What credits does the Venture X have?"
13
+ - **Wallet optimization**: "Which card should I use for travel?", "What gaps does my wallet have?"
14
+ - **Card recommendations**: "Best travel credit card for beginners", "No annual fee cash back card"
15
+ - **ROI / value**: "Is the Amex Platinum worth the $695 fee?", "First-year value of the Sapphire Preferred"
16
+ - **Award redemptions**: "Find business class flights JFK to Tokyo", "Search hotels in Paris with points"
17
+ - **Merchant lookup**: "What card should I use at Costco?", "Which card earns the most at gas stations?", "What MCC is a pharmacy?"
18
+
19
+ Live US credit card data via Brave Search. All commands output JSON for programmatic use.
20
+
21
+ ## MCC-enriched workflow
22
+
23
+ The bundled MCC dataset (981 codes, offline) enables a precise end-to-end flow:
24
+
25
+ ```
26
+ fleece wallet → identify category gaps
27
+ fleece mcc 5411 → confirm "Grocery Stores, Supermarkets"
28
+ fleece mcc 5411 --wallet → find best card for that exact merchant type
29
+ fleece recommend "grocery stores, gas, transit" → suggest a card to fill the gap
30
+ ```
31
+
32
+ **Common MCCs to know:**
33
+
34
+ | MCC | Category | Typical card bonus |
35
+ |------|----------|--------------------|
36
+ | 5411 | Grocery Stores | Amex Gold 4x, BofA Cash Rewards 3% |
37
+ | 5812 | Restaurants | Amex Gold 4x, CSP 3x |
38
+ | 5814 | Fast Food | Varies — not always same as 5812 |
39
+ | 5541 | Gas Stations | Citi Custom Cash 5x, BofA 3% |
40
+ | 4511 | Airlines | Amex Platinum 5x, CSR 3x |
41
+ | 7011 | Hotels | Amex Platinum 5x (Amex Travel), CSR 3x |
42
+ | 4111 | Transit / Commuter | CSR 3x, Bilt 3x |
43
+ | 5912 | Drugstores | Chase Freedom Flex 3x |
44
+
45
+ Use `fleece mcc <code>` (no API key needed) to resolve any MCC before running a rates or wallet query.
46
+
47
+ ## Prerequisites
48
+
49
+ ```bash
50
+ # Install once
51
+ pip install fleece-cli
52
+
53
+ # Set in environment or .env file
54
+ export BRAVE_API_KEY=<your_key>
55
+ ```
56
+
57
+ ## Commands
58
+
59
+ ### Full card report
60
+ ```bash
61
+ fleece card "<card name>" --json
62
+ ```
63
+ Returns fees, welcome offer, earning rates, credits, benefits, and strategy.
64
+
65
+ ### Earning rates
66
+ ```bash
67
+ fleece rates "<card name>" --json
68
+ fleece rates "<card name>" --category "<dining|travel|groceries|gas>" --json
69
+ ```
70
+
71
+ ### Transfer partners
72
+ ```bash
73
+ fleece partners "<card name>" --json
74
+ ```
75
+ Returns airline and hotel partners with ratios and transfer timing.
76
+
77
+ ### Statement credits
78
+ ```bash
79
+ fleece credits "<card name>" --json
80
+ ```
81
+ Returns all credits with amounts, cadence, and enrollment requirements.
82
+
83
+ ### Recent news (past month)
84
+ ```bash
85
+ fleece news "<card name>" --json
86
+ ```
87
+ Freshness-filtered to the past month.
88
+
89
+ ### Side-by-side comparison
90
+ ```bash
91
+ fleece compare "<card A>" "<card B>" --json
92
+ fleece compare "<card A>" "<card B>" --aspects "fees,rewards,credits" --json
93
+ ```
94
+
95
+ ### Portfolio / wallet analysis
96
+ ```bash
97
+ fleece wallet "<card 1>" "<card 2>" "<card 3>" --json
98
+ ```
99
+ Returns coverage map, overlaps, gaps, and next-card suggestions.
100
+
101
+ ### First-year ROI
102
+ ```bash
103
+ fleece roi "<card name>" --travel <monthly $> --dining <monthly $> --other <monthly $> --json
104
+ ```
105
+
106
+ ### Profile-based recommendations
107
+ ```bash
108
+ fleece recommend "<spending profile>" --json
109
+ fleece recommend "<spending profile>" --preferences "<preferences>" --json
110
+ ```
111
+
112
+ ## Output format
113
+
114
+ Every command with `--json` returns:
115
+ ```json
116
+ {
117
+ "command": "card",
118
+ "query": "...",
119
+ "result": "...",
120
+ "ok": true,
121
+ "error": null
122
+ }
123
+ ```
124
+
125
+ On error, `ok` is `false` and `error` contains the message. Always check `ok` before using `result`.
126
+
127
+ ## Exit codes
128
+
129
+ | Code | Meaning |
130
+ |------|---------|
131
+ | `0` | Success |
132
+ | `1` | Search / tool error (Brave API failure) |
133
+ | `2` | `BRAVE_API_KEY` not set |
134
+
135
+ ## Stdin piping
136
+
137
+ The primary argument on any single-card command accepts `-` to read from stdin:
138
+ ```bash
139
+ echo "Chase Sapphire Preferred" | fleece card - --json
140
+ echo "high dining spend" | fleece recommend - --json
141
+ ```
142
+
143
+ The `wallet` command accepts `-` as its sole argument to read newline-delimited card names:
144
+ ```bash
145
+ printf "Amex Gold\nChase Freedom Unlimited\nBilt\n" | fleece wallet - --json
146
+ ```
147
+
148
+ ## Coverage
149
+
150
+ Supports all major US issuers: Amex, Bank of America, Barclays, Bilt, Capital One,
151
+ Chase, Citi, Discover, Robinhood, U.S. Bank, Wells Fargo.
152
+
153
+ ## Redemption — PointsYeah URL generation
154
+
155
+ No API key required. These commands generate best-effort PointsYeah deep-link URLs
156
+ and optionally open them in the browser. Pure stdlib, no external calls.
157
+
158
+ ### Flight search
159
+ ```bash
160
+ fleece flights JFK LAX --date 2026-06-01 --json
161
+ fleece flights JFK LHR --date 2026-06-01 --return 2026-06-15 --adults 2 --cabin business --open
162
+ ```
163
+
164
+ Options: `--date` (required), `--return`, `--adults` (default 1), `--cabin` (economy | premium-economy | business | first), `--open`, `--json`
165
+
166
+ ### Hotel search
167
+ ```bash
168
+ fleece hotels "Tokyo" --checkin 2026-06-01 --checkout 2026-06-07 --json
169
+ fleece hotels "Jersey City" --checkin 2026-04-10 --checkout 2026-04-12 --guests 2 --rooms 1 --open
170
+ ```
171
+
172
+ Options: `--checkin` (required), `--checkout` (required), `--guests` (default 1), `--rooms` (default 1), `--open`, `--json`
173
+
174
+ ### JSON output format
175
+ ```json
176
+ {
177
+ "command": "flights",
178
+ "origin": "JFK", "destination": "LAX", "date": "2026-06-01",
179
+ "return_date": null, "adults": 1, "cabin": "economy",
180
+ "url": "https://www.pointsyeah.com/?type=flights&...",
181
+ "ok": true, "error": null
182
+ }
183
+ ```
184
+
185
+ > PointsYeah does not publish a stable deep-link spec. If the URL stops working,
186
+ > the query parameters still serve as a useful manual search reference.
@@ -29,3 +29,15 @@ fleece compare "Capital One Venture X" "Chase Sapphire Reserve" --json
29
29
  ```
30
30
 
31
31
  After receiving the result, synthesize a clear recommendation based on the user's stated priorities.
32
+
33
+ ## MCC-precise comparison
34
+
35
+ When comparing two cards on rewards, use `fleece mcc` to identify the exact merchant category first — then compare on that specific category for a more accurate result:
36
+
37
+ ```bash
38
+ # User wants to know which card earns more at Costco (a grocery/wholesale club)
39
+ fleece mcc 5411 # → Grocery Stores, Supermarkets
40
+
41
+ # Then compare with that category as the focus
42
+ fleece compare "Amex Gold" "Chase Sapphire Preferred" --aspects "grocery rewards,supermarket earning rate" --json
43
+ ```
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: fleece-flights
3
+ description: Generate a PointsYeah flight search URL. No API key required. Use when the user wants to search for award flights or find redemption options for a route.
4
+ ---
5
+
6
+ # /fleece-flights
7
+
8
+ Generates a PointsYeah flight search URL from origin, destination, and date. No Brave API key needed — pure URL generation.
9
+
10
+ ## When to use
11
+ - User asks to search for award flights on a route
12
+ - User wants to find redemption options after a `fleece wallet` or `fleece partners` analysis
13
+ - User provides an origin, destination, and date and wants to look up availability
14
+
15
+ ## Usage
16
+
17
+ ```bash
18
+ fleece flights <ORIGIN> <DESTINATION> --date <YYYY-MM-DD> --json
19
+ ```
20
+
21
+ **With return date and cabin:**
22
+ ```bash
23
+ fleece flights JFK LHR --date 2026-06-01 --return 2026-06-15 --adults 2 --cabin business --json
24
+ ```
25
+
26
+ **Open in browser:**
27
+ ```bash
28
+ fleece flights JFK LAX --date 2026-06-01 --open
29
+ ```
30
+
31
+ Cabin options: `economy` | `premium-economy` | `business` | `first` (default: `economy`)
32
+
33
+ ## Output
34
+
35
+ ```json
36
+ {
37
+ "command": "flights",
38
+ "origin": "JFK",
39
+ "destination": "LAX",
40
+ "date": "2026-06-01",
41
+ "return_date": null,
42
+ "adults": 1,
43
+ "cabin": "economy",
44
+ "url": "https://www.pointsyeah.com/?type=flights&origin=JFK&destination=LAX&date=2026-06-01&return=&adults=1&cabin=economy",
45
+ "ok": true,
46
+ "error": null
47
+ }
48
+ ```
49
+
50
+ > PointsYeah does not publish a stable deep-link spec. If the URL stops resolving,
51
+ > the query parameters still serve as a useful manual search reference.
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: fleece-hotels
3
+ description: Generate a PointsYeah hotel search URL. No API key required. Use when the user wants to search for award hotel stays or find redemption options for a destination.
4
+ ---
5
+
6
+ # /fleece-hotels
7
+
8
+ Generates a PointsYeah hotel search URL from location and dates. No Brave API key needed — pure URL generation.
9
+
10
+ ## When to use
11
+ - User asks to search for award hotel stays at a destination
12
+ - User wants to redeem hotel points after a `fleece wallet` or `fleece partners` analysis
13
+ - User provides a location and dates and wants to look up availability
14
+
15
+ ## Usage
16
+
17
+ ```bash
18
+ fleece hotels "<location>" --checkin <YYYY-MM-DD> --checkout <YYYY-MM-DD> --json
19
+ ```
20
+
21
+ **With guests and rooms:**
22
+ ```bash
23
+ fleece hotels "Tokyo" --checkin 2026-06-01 --checkout 2026-06-07 --guests 2 --rooms 1 --json
24
+ ```
25
+
26
+ **Open in browser:**
27
+ ```bash
28
+ fleece hotels "Jersey City" --checkin 2026-04-10 --checkout 2026-04-12 --open
29
+ ```
30
+
31
+ ## Output
32
+
33
+ ```json
34
+ {
35
+ "command": "hotels",
36
+ "location": "Tokyo",
37
+ "checkin": "2026-06-01",
38
+ "checkout": "2026-06-07",
39
+ "guests": 1,
40
+ "rooms": 1,
41
+ "url": "https://www.pointsyeah.com/?type=hotels&location=Tokyo&checkin=2026-06-01&checkout=2026-06-07&guests=1&rooms=1",
42
+ "ok": true,
43
+ "error": null
44
+ }
45
+ ```
46
+
47
+ > PointsYeah does not publish a stable deep-link spec. If the URL stops resolving,
48
+ > the query parameters still serve as a useful manual search reference.
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: fleece-mcc
3
+ description: Look up a Merchant Category Code (MCC) and find the best card in the user's wallet to use at that merchant. No API key needed for basic lookup; BRAVE_API_KEY needed for wallet cross-reference. Use when the user asks "what card should I use at [merchant]?" or provides an MCC code.
4
+ ---
5
+
6
+ # /fleece-mcc
7
+
8
+ Looks up an MCC code from the bundled dataset (981 codes, offline) and optionally cross-references with the user's saved wallet to find the highest-earning card.
9
+
10
+ ## When to use
11
+ - User asks "what card should I use at [merchant type]?" and you know or can infer the MCC
12
+ - User provides an MCC code directly and wants to know the category
13
+ - User wants to know which card earns most at a specific merchant category
14
+ - After `fleece wallet` identifies a gap, find which MCC codes fall into that gap
15
+
16
+ ## Usage
17
+
18
+ **Basic lookup (no API key needed):**
19
+ ```bash
20
+ fleece mcc 5812
21
+ # → MCC 5812: Eating Places, Restaurants
22
+ ```
23
+
24
+ **With wallet cross-reference (requires BRAVE_API_KEY):**
25
+ ```bash
26
+ fleece mcc 5812 --wallet --json
27
+ fleece mcc 5411 --wallet --json # Grocery Stores
28
+ fleece mcc 5541 --wallet --json # Service Stations (gas)
29
+ fleece mcc 4111 --wallet --json # Transportation / Commuter
30
+ ```
31
+
32
+ ## Common MCCs
33
+
34
+ | MCC | Category |
35
+ |------|----------|
36
+ | 5411 | Grocery Stores, Supermarkets |
37
+ | 5812 | Eating Places, Restaurants |
38
+ | 5541 | Service Stations (gas) |
39
+ | 5912 | Drug Stores, Pharmacies |
40
+ | 5310 | Discount Stores |
41
+ | 5732 | Electronics Stores |
42
+ | 4111 | Transportation / Commuter |
43
+ | 4411 | Cruise Lines |
44
+ | 7011 | Hotels, Motels, Resorts |
45
+ | 4511 | Airlines, Air Carriers |
46
+
47
+ ## Output (--json)
48
+
49
+ ```json
50
+ {
51
+ "command": "mcc",
52
+ "mcc": "5812",
53
+ "category": "Eating Places, Restaurants",
54
+ "irs_description": "Restaurants",
55
+ "ok": true,
56
+ "error": null
57
+ }
58
+ ```
59
+
60
+ With `--wallet`, returns live research on earning rates per card plus a recommendation for the best card to use.
61
+
62
+ ## Notes
63
+ - Dataset is bundled with `fleece-cli` — works fully offline for basic lookups
64
+ - Source: github.com/greggles/mcc-codes (981 codes)
65
+ - `--wallet` mode requires cards saved via `fleece cards add`
@@ -27,3 +27,13 @@ Categories: `dining`, `travel`, `groceries`, `gas`, `streaming`, `drugstores`, e
27
27
  fleece rates "Chase Sapphire Preferred" --json
28
28
  fleece rates "Amex Gold" --category dining --json
29
29
  ```
30
+
31
+ ## MCC precision tip
32
+
33
+ Category names like `dining` are broad — a card may earn differently at MCC 5812 (sit-down restaurants) vs 5814 (fast food) vs 5411 (grocery stores with a café). When exact merchant-level precision matters, pair with `fleece mcc`:
34
+
35
+ ```bash
36
+ # Confirm the MCC for the merchant type, then look up the rate
37
+ fleece mcc 5814 # → Fast Food Restaurants
38
+ fleece rates "Amex Gold" --category "fast food restaurants" --json
39
+ ```
@@ -30,3 +30,19 @@ fleece recommend "frequent international traveler" --preferences "priority pass
30
30
  ```
31
31
 
32
32
  After receiving results, present 2–3 top recommendations with a brief rationale for each.
33
+
34
+ ## MCC-informed spending profiles
35
+
36
+ For more precise recommendations, identify the MCC codes for the user's top merchants first, then describe the profile by category name:
37
+
38
+ ```bash
39
+ # User shops at Whole Foods (5411), commutes by subway (4111), eats out often (5812)
40
+ fleece mcc 5411 # → Grocery Stores, Supermarkets
41
+ fleece mcc 4111 # → Transportation / Commuter
42
+ fleece mcc 5812 # → Eating Places, Restaurants
43
+
44
+ # Then recommend based on confirmed categories
45
+ fleece recommend "grocery stores, transit, restaurants" --json
46
+ ```
47
+
48
+ This avoids vague profiles like "I spend a lot on food" and maps the user's real spend to known card bonus categories.
@@ -60,3 +60,20 @@ fleece wallet
60
60
  # Explicit cards
61
61
  fleece wallet "Amex Gold" "Chase Sapphire Preferred" "Citi Double Cash"
62
62
  ```
63
+
64
+ ## MCC-enriched gap analysis
65
+
66
+ After `fleece wallet` identifies a gap (e.g., "no bonus on grocery stores"), use `fleece mcc` to drill into the precise merchant category and find the best card for it:
67
+
68
+ ```bash
69
+ # Wallet says "gap on groceries" → confirm the MCC and cross-reference wallet
70
+ fleece mcc 5411 --wallet --json # Grocery Stores, Supermarkets
71
+
72
+ # Other common gap MCCs to check after a wallet analysis
73
+ fleece mcc 5541 --wallet # Gas stations
74
+ fleece mcc 5912 --wallet # Drugstores / pharmacies
75
+ fleece mcc 4111 --wallet # Transit / commuter
76
+ fleece mcc 5812 --wallet # Restaurants
77
+ ```
78
+
79
+ This turns a vague "gap on groceries" into a precise card recommendation for a specific merchant category.
@@ -11,7 +11,7 @@ jobs:
11
11
  steps:
12
12
  - uses: actions/checkout@v5
13
13
 
14
- - uses: actions/setup-python@v5
14
+ - uses: actions/setup-python@v6
15
15
  with:
16
16
  python-version: "3.12"
17
17
 
@@ -19,7 +19,7 @@ jobs:
19
19
 
20
20
  - run: python -m build
21
21
 
22
- - uses: actions/upload-artifact@v5
22
+ - uses: actions/upload-artifact@v6
23
23
  with:
24
24
  name: dist
25
25
  path: dist/
@@ -31,7 +31,7 @@ jobs:
31
31
  permissions:
32
32
  id-token: write
33
33
  steps:
34
- - uses: actions/download-artifact@v5
34
+ - uses: actions/download-artifact@v6
35
35
  with:
36
36
  name: dist
37
37
  path: dist/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fleece-cli
3
- Version: 0.2.0
3
+ Version: 0.3.1
4
4
  Summary: Credit card research and redemption CLI — live data via Brave Search, PointsYeah URL generation, designed for humans and AI agents.
5
5
  Author-email: Yuan Chen <cysbc1999@gmail.com>
6
6
  License: MIT
@@ -4,10 +4,13 @@ Fleece CLI — agent-friendly companion to the Fleece Streamlit chatbot.
4
4
  Exposes credit card research tools as shell commands backed by Brave Search.
5
5
  Designed for use by AI agents (Claude Code, Codex) and humans alike.
6
6
 
7
+ BRAVE_API_KEY is optional — commands that don't need live search (mcc, flights,
8
+ hotels) work fully offline. Research commands (card, rates, wallet, …) require it.
9
+
7
10
  Exit codes:
8
11
  0 success
9
12
  1 search / tool error
10
- 2 configuration error (missing API key)
13
+ 2 BRAVE_API_KEY required but not set
11
14
  """
12
15
  import json
13
16
  import sys
@@ -18,7 +21,11 @@ from dotenv import load_dotenv
18
21
 
19
22
  app = typer.Typer(
20
23
  name="fleece",
21
- help="Fleece credit card research CLI — live data via Brave Search.",
24
+ help=(
25
+ "Fleece credit card research CLI — live data via Brave Search.\n\n"
26
+ "BRAVE_API_KEY is optional: mcc, flights, and hotels work offline.\n"
27
+ "Research commands (card, rates, wallet, …) require it."
28
+ ),
22
29
  no_args_is_help=True,
23
30
  )
24
31
 
@@ -27,20 +34,21 @@ app = typer.Typer(
27
34
  # ---------------------------------------------------------------------------
28
35
 
29
36
  def _resolve_key(api_key: Optional[str], no_dotenv: bool) -> str:
30
- """Load env and return the Brave API key, or exit with code 2."""
37
+ """Load env and return the Brave API key (may be empty checked at use time)."""
31
38
  import os
32
39
  if not no_dotenv:
33
40
  load_dotenv()
34
- key = api_key or os.getenv("BRAVE_API_KEY", "")
41
+ return api_key or os.getenv("BRAVE_API_KEY", "")
42
+
43
+
44
+ def _get_wrapper(key: str, freshness: Optional[str] = None):
35
45
  if not key:
36
46
  _error_exit(
37
- "BRAVE_API_KEY not set. Pass --api-key or add it to .env.",
47
+ "BRAVE_API_KEY is required for live research. "
48
+ "Set it in your environment, add it to .env, or pass --api-key. "
49
+ "Commands mcc, flights, and hotels work without it.",
38
50
  code=2,
39
51
  )
40
- return key
41
-
42
-
43
- def _get_wrapper(key: str, freshness: Optional[str] = None):
44
52
  from tools.brave_client import build_brave_wrapper
45
53
  return build_brave_wrapper(key, freshness=freshness)
46
54
 
@@ -309,6 +317,87 @@ def recommend(
309
317
  _run_search(_get_wrapper(key), query, "recommend", as_json)
310
318
 
311
319
 
320
+ # ---------------------------------------------------------------------------
321
+ # MCC lookup — offline, no API key required
322
+ # ---------------------------------------------------------------------------
323
+
324
+ def _load_mcc_db() -> dict[str, dict]:
325
+ """Load mcc_codes.jsonl bundled with the package. Returns {mcc: record}."""
326
+ import importlib.resources, pathlib
327
+ # Try installed package data first, fall back to local file
328
+ for candidate in [
329
+ pathlib.Path(__file__).parent / "mcc_codes.jsonl",
330
+ ]:
331
+ if candidate.exists():
332
+ data = {}
333
+ for line in candidate.read_text().splitlines():
334
+ line = line.strip()
335
+ if line:
336
+ rec = json.loads(line)
337
+ data[str(rec["mcc"]).zfill(4)] = rec
338
+ return data
339
+ _error_exit("mcc_codes.jsonl not found. Re-install fleece-cli.", code=1)
340
+
341
+
342
+ @app.command()
343
+ def mcc(
344
+ code: Annotated[str, typer.Argument(help="4-digit MCC code, e.g. 5812.")],
345
+ wallet: Annotated[bool, typer.Option("--wallet", "-w", help="Cross-reference with your saved cards.")] = False,
346
+ api_key: ApiKeyOpt = None,
347
+ as_json: JsonOpt = False,
348
+ no_dotenv: NoDotenv = False,
349
+ ):
350
+ """Look up a Merchant Category Code and find the best card in your wallet to use."""
351
+ code = code.strip().zfill(4)
352
+ db = _load_mcc_db()
353
+
354
+ if code not in db:
355
+ _error_exit(f"MCC {code} not found in dataset.", code=1)
356
+
357
+ rec = db[code]
358
+ category = rec.get("edited_description") or rec.get("combined_description", "Unknown")
359
+ irs_desc = rec.get("irs_description", "")
360
+
361
+ if not wallet:
362
+ if as_json:
363
+ typer.echo(json.dumps({"command": "mcc", "mcc": code, "category": category,
364
+ "irs_description": irs_desc, "ok": True, "error": None}))
365
+ else:
366
+ typer.echo(f"MCC {code}: {category}")
367
+ if irs_desc and irs_desc != category:
368
+ typer.echo(f"IRS description: {irs_desc}")
369
+ return
370
+
371
+ # Wallet mode — look up earning rates for each card in the profile
372
+ card_names = _profile_card_names()
373
+ if not card_names:
374
+ _error_exit("No cards in profile. Add cards with: fleece cards add \"<name>\"", code=1)
375
+
376
+ key = _resolve_key(api_key, no_dotenv)
377
+ from tools.brave_client import search_and_format
378
+ wrapper = _get_wrapper(key)
379
+
380
+ parts = []
381
+ try:
382
+ for name in card_names:
383
+ result = search_and_format(
384
+ wrapper,
385
+ f'"{name}" credit card earning rate "{category}" OR "{irs_desc}" category multiplier 2025',
386
+ max_results=2,
387
+ )
388
+ parts.append(f"### {name}\n{result}")
389
+ except Exception as e:
390
+ _error_exit(str(e), code=1)
391
+
392
+ combined = (
393
+ f"MCC {code}: {category}\n\n"
394
+ + "\n\n".join(parts)
395
+ + f"\n\nBased on the above, which card earns the most at merchants coded as MCC {code} ({category})? "
396
+ "Show the multiplier for each card and pick the winner."
397
+ )
398
+ _emit(combined, as_json, "mcc", code)
399
+
400
+
312
401
  # ---------------------------------------------------------------------------
313
402
  # Redemption — PointsYeah URL generation (no API key required)
314
403
  # ---------------------------------------------------------------------------