fleece-cli 0.1.0__tar.gz → 0.2.0__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 (54) hide show
  1. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/.agents/skills/fleece/SKILL.md +19 -19
  2. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/.claude/skills/fleece-card.md +3 -3
  3. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/.claude/skills/fleece-compare.md +4 -4
  4. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/.claude/skills/fleece-credits.md +3 -3
  5. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/.claude/skills/fleece-news.md +3 -3
  6. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/.claude/skills/fleece-partners.md +4 -4
  7. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/.claude/skills/fleece-rates.md +4 -4
  8. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/.claude/skills/fleece-recommend.md +5 -5
  9. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/.claude/skills/fleece-roi.md +3 -3
  10. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/.claude/skills/fleece-wallet.md +5 -5
  11. fleece_cli-0.2.0/.github/workflows/publish-skills.yml +29 -0
  12. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/.github/workflows/publish.yml +3 -3
  13. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/CLAUDE.md +7 -2
  14. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/PKG-INFO +2 -2
  15. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/cli.py +67 -0
  16. fleece_cli-0.2.0/docs/CNAME +1 -0
  17. fleece_cli-0.2.0/docs/index.html +590 -0
  18. fleece_cli-0.2.0/pointsyeah.py +58 -0
  19. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/pyproject.toml +3 -3
  20. fleece_cli-0.2.0/tests/test_pointsyeah.py +31 -0
  21. fleece_cli-0.1.0/.github/workflows/databricks-app-deploy.yml +0 -40
  22. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/.devcontainer/devcontainer.json +0 -0
  23. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/.gitignore +0 -0
  24. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/.streamlit/config.toml +0 -0
  25. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/Advertiser Disclosure.md +0 -0
  26. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/KnowledgeCatalog.json +0 -0
  27. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/LICENSE +0 -0
  28. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/MyCards.json +0 -0
  29. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/README.md +0 -0
  30. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/app.yaml +0 -0
  31. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/assets/default_card.png +0 -0
  32. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/assets/default_card.svg +0 -0
  33. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/db.py +0 -0
  34. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/fleece poc.ipynb +0 -0
  35. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/fleece.py +0 -0
  36. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/image_service.py +0 -0
  37. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/install.sh +0 -0
  38. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/migrate.py +0 -0
  39. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/pages/credit_cards.py +0 -0
  40. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/pages/my_credit_cards.py +0 -0
  41. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/plan.md +0 -0
  42. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/prompts/agent_system_prompt.py +0 -0
  43. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/reference/Analyzer.md +0 -0
  44. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/requirements-test.txt +0 -0
  45. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/requirements.txt +0 -0
  46. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/style.css +0 -0
  47. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/test_fleece.py +0 -0
  48. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/tests/__init__.py +0 -0
  49. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/tests/test_brave_client.py +0 -0
  50. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/tests/test_cli.py +0 -0
  51. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/tests/test_tools.py +0 -0
  52. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/tools/__init__.py +0 -0
  53. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/tools/brave_client.py +0 -0
  54. {fleece_cli-0.1.0 → fleece_cli-0.2.0}/tools/credit_card_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: fleece
3
- description: Fleece credit card research CLI. Provides live US credit card data via Brave Search — full reports, earning rates, transfer partners, statement credits, recent news, card comparisons, portfolio analysis, ROI estimates, and profile-based recommendations. Use whenever you need current credit card information.
3
+ description: Fleece credit card research CLI. Provides live US credit card data via Brave Search — full reports, earning rates, transfer partners, statement credits, recent news, card comparisons, portfolio analysis, ROI estimates, and profile-based recommendations. Install with `pip install fleece-cli`. Use whenever you need current credit card information.
4
4
  ---
5
5
 
6
6
  # Fleece Credit Card Research
@@ -11,66 +11,66 @@ programmatic use. Requires `BRAVE_API_KEY` in the environment.
11
11
  ## Prerequisites
12
12
 
13
13
  ```bash
14
+ # Install once
15
+ pip install fleece-cli
16
+
14
17
  # Set in environment or .env file
15
18
  export BRAVE_API_KEY=<your_key>
16
-
17
- # Run from the fleece project root
18
- cd /path/to/fleece
19
19
  ```
20
20
 
21
21
  ## Commands
22
22
 
23
23
  ### Full card report
24
24
  ```bash
25
- python cli.py card "<card name>" --json
25
+ fleece card "<card name>" --json
26
26
  ```
27
27
  Returns fees, welcome offer, earning rates, credits, benefits, and strategy.
28
28
 
29
29
  ### Earning rates
30
30
  ```bash
31
- python cli.py rates "<card name>" --json
32
- python cli.py rates "<card name>" --category "<dining|travel|groceries|gas>" --json
31
+ fleece rates "<card name>" --json
32
+ fleece rates "<card name>" --category "<dining|travel|groceries|gas>" --json
33
33
  ```
34
34
 
35
35
  ### Transfer partners
36
36
  ```bash
37
- python cli.py partners "<card name>" --json
37
+ fleece partners "<card name>" --json
38
38
  ```
39
39
  Returns airline and hotel partners with ratios and transfer timing.
40
40
 
41
41
  ### Statement credits
42
42
  ```bash
43
- python cli.py credits "<card name>" --json
43
+ fleece credits "<card name>" --json
44
44
  ```
45
45
  Returns all credits with amounts, cadence, and enrollment requirements.
46
46
 
47
47
  ### Recent news (past month)
48
48
  ```bash
49
- python cli.py news "<card name>" --json
49
+ fleece news "<card name>" --json
50
50
  ```
51
51
  Freshness-filtered to the past month.
52
52
 
53
53
  ### Side-by-side comparison
54
54
  ```bash
55
- python cli.py compare "<card A>" "<card B>" --json
56
- python cli.py compare "<card A>" "<card B>" --aspects "fees,rewards,credits" --json
55
+ fleece compare "<card A>" "<card B>" --json
56
+ fleece compare "<card A>" "<card B>" --aspects "fees,rewards,credits" --json
57
57
  ```
58
58
 
59
59
  ### Portfolio / wallet analysis
60
60
  ```bash
61
- python cli.py wallet "<card 1>" "<card 2>" "<card 3>" --json
61
+ fleece wallet "<card 1>" "<card 2>" "<card 3>" --json
62
62
  ```
63
63
  Returns coverage map, overlaps, gaps, and next-card suggestions.
64
64
 
65
65
  ### First-year ROI
66
66
  ```bash
67
- python cli.py roi "<card name>" --travel <monthly $> --dining <monthly $> --other <monthly $> --json
67
+ fleece roi "<card name>" --travel <monthly $> --dining <monthly $> --other <monthly $> --json
68
68
  ```
69
69
 
70
70
  ### Profile-based recommendations
71
71
  ```bash
72
- python cli.py recommend "<spending profile>" --json
73
- python cli.py recommend "<spending profile>" --preferences "<preferences>" --json
72
+ fleece recommend "<spending profile>" --json
73
+ fleece recommend "<spending profile>" --preferences "<preferences>" --json
74
74
  ```
75
75
 
76
76
  ## Output format
@@ -100,13 +100,13 @@ On error, `ok` is `false` and `error` contains the message. Always check `ok` be
100
100
 
101
101
  The primary argument on any single-card command accepts `-` to read from stdin:
102
102
  ```bash
103
- echo "Chase Sapphire Preferred" | python cli.py card - --json
104
- echo "high dining spend" | python cli.py recommend - --json
103
+ echo "Chase Sapphire Preferred" | fleece card - --json
104
+ echo "high dining spend" | fleece recommend - --json
105
105
  ```
106
106
 
107
107
  The `wallet` command accepts `-` as its sole argument to read newline-delimited card names:
108
108
  ```bash
109
- printf "Amex Gold\nChase Freedom Unlimited\nBilt\n" | python cli.py wallet - --json
109
+ printf "Amex Gold\nChase Freedom Unlimited\nBilt\n" | fleece wallet - --json
110
110
  ```
111
111
 
112
112
  ## Coverage
@@ -15,7 +15,7 @@ Fetches a comprehensive report for a US credit card using live Brave Search data
15
15
  ## Usage
16
16
 
17
17
  ```bash
18
- python cli.py card "<card name>" --json
18
+ fleece card "<card name>" --json
19
19
  ```
20
20
 
21
21
  The `--json` flag returns a structured envelope your can parse:
@@ -33,6 +33,6 @@ Use `result` as your research context, then synthesize a natural answer.
33
33
  ## Example
34
34
 
35
35
  ```bash
36
- python cli.py card "Chase Sapphire Preferred" --json
37
- python cli.py card "Amex Gold" --json
36
+ fleece card "Chase Sapphire Preferred" --json
37
+ fleece card "Amex Gold" --json
38
38
  ```
@@ -15,8 +15,8 @@ Searches live data for both cards and returns research for a structured comparis
15
15
  ## Usage
16
16
 
17
17
  ```bash
18
- python cli.py compare "<card A>" "<card B>" --json
19
- python cli.py compare "<card A>" "<card B>" --aspects "fees,rewards,credits" --json
18
+ fleece compare "<card A>" "<card B>" --json
19
+ fleece compare "<card A>" "<card B>" --aspects "fees,rewards,credits" --json
20
20
  ```
21
21
 
22
22
  Default aspects: `fees,rewards,welcome_offer,credits,transfer_partners`
@@ -24,8 +24,8 @@ Default aspects: `fees,rewards,welcome_offer,credits,transfer_partners`
24
24
  ## Example
25
25
 
26
26
  ```bash
27
- python cli.py compare "Amex Gold" "Chase Sapphire Preferred" --json
28
- python cli.py compare "Capital One Venture X" "Chase Sapphire Reserve" --json
27
+ fleece compare "Amex Gold" "Chase Sapphire Preferred" --json
28
+ 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.
@@ -15,12 +15,12 @@ Fetches the full list of statement credits and perks for a card.
15
15
  ## Usage
16
16
 
17
17
  ```bash
18
- python cli.py credits "<card name>" --json
18
+ fleece credits "<card name>" --json
19
19
  ```
20
20
 
21
21
  ## Example
22
22
 
23
23
  ```bash
24
- python cli.py credits "Amex Platinum" --json
25
- python cli.py credits "Chase Sapphire Reserve" --json
24
+ fleece credits "Amex Platinum" --json
25
+ fleece credits "Chase Sapphire Reserve" --json
26
26
  ```
@@ -15,7 +15,7 @@ Searches for the latest news and changes for a card using freshness-filtered res
15
15
  ## Usage
16
16
 
17
17
  ```bash
18
- python cli.py news "<card name>" --json
18
+ fleece news "<card name>" --json
19
19
  ```
20
20
 
21
21
  Results are filtered to the past month (`freshness=pm`).
@@ -23,6 +23,6 @@ Results are filtered to the past month (`freshness=pm`).
23
23
  ## Example
24
24
 
25
25
  ```bash
26
- python cli.py news "Amex Gold" --json
27
- python cli.py news "Chase Sapphire Reserve" --json
26
+ fleece news "Amex Gold" --json
27
+ fleece news "Chase Sapphire Reserve" --json
28
28
  ```
@@ -15,13 +15,13 @@ Fetches transfer partner details for a card's rewards currency.
15
15
  ## Usage
16
16
 
17
17
  ```bash
18
- python cli.py partners "<card name>" --json
18
+ fleece partners "<card name>" --json
19
19
  ```
20
20
 
21
21
  ## Example
22
22
 
23
23
  ```bash
24
- python cli.py partners "Chase Sapphire Preferred" --json
25
- python cli.py partners "Capital One Venture X" --json
26
- python cli.py partners "Amex Platinum" --json
24
+ fleece partners "Chase Sapphire Preferred" --json
25
+ fleece partners "Capital One Venture X" --json
26
+ fleece partners "Amex Platinum" --json
27
27
  ```
@@ -15,8 +15,8 @@ Looks up earning rates for a credit card, with optional category filtering.
15
15
  ## Usage
16
16
 
17
17
  ```bash
18
- python cli.py rates "<card name>" --json
19
- python cli.py rates "<card name>" --category "<category>" --json
18
+ fleece rates "<card name>" --json
19
+ fleece rates "<card name>" --category "<category>" --json
20
20
  ```
21
21
 
22
22
  Categories: `dining`, `travel`, `groceries`, `gas`, `streaming`, `drugstores`, etc.
@@ -24,6 +24,6 @@ Categories: `dining`, `travel`, `groceries`, `gas`, `streaming`, `drugstores`, e
24
24
  ## Example
25
25
 
26
26
  ```bash
27
- python cli.py rates "Chase Sapphire Preferred" --json
28
- python cli.py rates "Amex Gold" --category dining --json
27
+ fleece rates "Chase Sapphire Preferred" --json
28
+ fleece rates "Amex Gold" --category dining --json
29
29
  ```
@@ -15,8 +15,8 @@ Searches for best-match US credit cards for a given spending profile.
15
15
  ## Usage
16
16
 
17
17
  ```bash
18
- python cli.py recommend "<spending profile>" --json
19
- python cli.py recommend "<spending profile>" --preferences "<extra preferences>" --json
18
+ fleece recommend "<spending profile>" --json
19
+ fleece recommend "<spending profile>" --preferences "<extra preferences>" --json
20
20
  ```
21
21
 
22
22
  Keep the profile concise: categories + rough amounts or priorities.
@@ -24,9 +24,9 @@ Keep the profile concise: categories + rough amounts or priorities.
24
24
  ## Example
25
25
 
26
26
  ```bash
27
- python cli.py recommend "high dining and travel spend, $500/mo dining, $300/mo travel" --json
28
- python cli.py recommend "everyday spending, mostly groceries and gas" --preferences "no annual fee" --json
29
- python cli.py recommend "frequent international traveler" --preferences "priority pass lounge access" --json
27
+ fleece recommend "high dining and travel spend, $500/mo dining, $300/mo travel" --json
28
+ fleece recommend "everyday spending, mostly groceries and gas" --preferences "no annual fee" --json
29
+ fleece recommend "frequent international traveler" --preferences "priority pass lounge access" --json
30
30
  ```
31
31
 
32
32
  After receiving results, present 2–3 top recommendations with a brief rationale for each.
@@ -15,7 +15,7 @@ Estimates first-year return on investment for a card based on the user's spendin
15
15
  ## Usage
16
16
 
17
17
  ```bash
18
- python cli.py roi "<card name>" --travel <monthly $> --dining <monthly $> --other <monthly $> --json
18
+ fleece roi "<card name>" --travel <monthly $> --dining <monthly $> --other <monthly $> --json
19
19
  ```
20
20
 
21
21
  All spend flags are optional and default to `0`. Omit any category the user didn't mention.
@@ -23,8 +23,8 @@ All spend flags are optional and default to `0`. Omit any category the user didn
23
23
  ## Example
24
24
 
25
25
  ```bash
26
- python cli.py roi "Chase Sapphire Preferred" --travel 500 --dining 300 --other 1000 --json
27
- python cli.py roi "Amex Gold" --dining 800 --other 500 --json
26
+ fleece roi "Chase Sapphire Preferred" --travel 500 --dining 300 --other 1000 --json
27
+ fleece roi "Amex Gold" --dining 800 --other 500 --json
28
28
  ```
29
29
 
30
30
  The result includes annual spend totals, an assumed cents-per-point value, and live research context.
@@ -18,19 +18,19 @@ Analyzes a multi-card portfolio and identifies optimization opportunities.
18
18
  **If the user has a saved profile (most common)** — no args needed; the CLI auto-loads from `fleece.db`:
19
19
 
20
20
  ```bash
21
- python cli.py wallet
21
+ fleece wallet
22
22
  ```
23
23
 
24
24
  **To pass cards explicitly:**
25
25
 
26
26
  ```bash
27
- python cli.py wallet "Amex Platinum" "Chase Freedom Unlimited" "Bilt"
27
+ fleece wallet "Amex Platinum" "Chase Freedom Unlimited" "Bilt"
28
28
  ```
29
29
 
30
30
  **Agent-friendly JSON output:**
31
31
 
32
32
  ```bash
33
- python cli.py wallet --json
33
+ fleece wallet --json
34
34
  ```
35
35
 
36
36
  Cards can come from three sources (in priority order):
@@ -55,8 +55,8 @@ The result includes:
55
55
 
56
56
  ```bash
57
57
  # Auto-load saved wallet (preferred)
58
- python cli.py wallet
58
+ fleece wallet
59
59
 
60
60
  # Explicit cards
61
- python cli.py wallet "Amex Gold" "Chase Sapphire Preferred" "Citi Double Cash"
61
+ fleece wallet "Amex Gold" "Chase Sapphire Preferred" "Citi Double Cash"
62
62
  ```
@@ -0,0 +1,29 @@
1
+ name: Publish Skills to ClawHub
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - ".agents/skills/**"
9
+ - ".claude/skills/**"
10
+ workflow_dispatch:
11
+
12
+ jobs:
13
+ publish:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v5
17
+
18
+ - uses: actions/setup-node@v5
19
+ with:
20
+ node-version: "20"
21
+
22
+ - name: Install clawhub
23
+ run: npm install -g clawhub
24
+
25
+ - name: Authenticate
26
+ run: clawhub login --token "${{ secrets.CLAWHUB_TOKEN }}" --no-browser
27
+
28
+ - name: Publish skills
29
+ run: clawhub sync --root .agents/skills --all --changelog "Automated publish from ${{ github.sha }}"
@@ -9,7 +9,7 @@ jobs:
9
9
  build:
10
10
  runs-on: ubuntu-latest
11
11
  steps:
12
- - uses: actions/checkout@v4
12
+ - uses: actions/checkout@v5
13
13
 
14
14
  - uses: actions/setup-python@v5
15
15
  with:
@@ -19,7 +19,7 @@ jobs:
19
19
 
20
20
  - run: python -m build
21
21
 
22
- - uses: actions/upload-artifact@v4
22
+ - uses: actions/upload-artifact@v5
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@v4
34
+ - uses: actions/download-artifact@v5
35
35
  with:
36
36
  name: dist
37
37
  path: dist/
@@ -33,12 +33,17 @@ Fleece is a chatbot that:
33
33
  - **No structured research**: Lacks the ability to provide specific, verified credit card information (fees, benefits, offers, transfer partners, etc.)
34
34
 
35
35
  ## Future Integration Opportunities
36
- - **credit-card-skills integration**: Add specialized research capabilities to access live issuer data
37
36
  - **Multi-page app expansion**: Build out the "Credit Cards" discovery page and "My Credit Cards" portfolio management
38
37
  - **Database backend**: Store user profiles, card portfolios, and preferences
39
38
 
39
+ ## CLI — Redemption Commands
40
+ The `fleece flights` and `fleece hotels` commands are merged from the former `pointsyeah-cli` project (now archived). They generate PointsYeah search URLs with no API key required — pure stdlib, zero external dependencies. Source lives in `pointsyeah.py`.
41
+
40
42
  ## Development Notes
41
43
  - Author: Yuan Chen
42
44
  - Created: March 16, 2025
43
45
  - Uses custom CSS styling (style.css)
44
- - Requires OpenAI API key (entered via sidebar, not stored)
46
+ - Requires OpenAI API key (entered via sidebar, not stored)
47
+
48
+ ## Infrastructure Notes
49
+ - **Databricks**: No Databricks resources are available at the moment. The Databricks App deployment workflow has been removed. Do not suggest or implement Databricks-based solutions until resources are provisioned.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fleece-cli
3
- Version: 0.1.0
4
- Summary: Credit card research CLI — live data via Brave Search, designed for humans and AI agents.
3
+ Version: 0.2.0
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
7
7
  License-File: LICENSE
@@ -309,6 +309,73 @@ def recommend(
309
309
  _run_search(_get_wrapper(key), query, "recommend", as_json)
310
310
 
311
311
 
312
+ # ---------------------------------------------------------------------------
313
+ # Redemption — PointsYeah URL generation (no API key required)
314
+ # ---------------------------------------------------------------------------
315
+
316
+ CABINS = ["economy", "premium-economy", "business", "first"]
317
+
318
+
319
+ @app.command()
320
+ def flights(
321
+ origin: Annotated[str, typer.Argument(help="Origin airport code (e.g. JFK).")],
322
+ destination: Annotated[str, typer.Argument(help="Destination airport code (e.g. LAX).")],
323
+ date: Annotated[str, typer.Option("--date", help="Departure date (YYYY-MM-DD).")],
324
+ return_date: Annotated[Optional[str], typer.Option("--return", help="Return date (YYYY-MM-DD).")] = None,
325
+ adults: Annotated[int, typer.Option("--adults", min=1)] = 1,
326
+ cabin: Annotated[str, typer.Option("--cabin", help=f"Cabin class: {', '.join(CABINS)}.")] = "economy",
327
+ open_url: Annotated[bool, typer.Option("--open", help="Open URL in browser.")] = False,
328
+ as_json: JsonOpt = False,
329
+ ):
330
+ """Generate a PointsYeah flight search URL and optionally open it."""
331
+ import webbrowser
332
+ from pointsyeah import FlightsQuery, build_flights_url
333
+
334
+ if cabin not in CABINS:
335
+ _error_exit(f"Invalid cabin '{cabin}'. Choose from: {', '.join(CABINS)}")
336
+
337
+ q = FlightsQuery(origin=origin, destination=destination, date=date,
338
+ return_date=return_date, adults=adults, cabin=cabin)
339
+ url = build_flights_url(q)
340
+
341
+ if as_json:
342
+ import dataclasses
343
+ typer.echo(json.dumps({"command": "flights", **dataclasses.asdict(q), "url": url, "ok": True, "error": None}))
344
+ else:
345
+ typer.echo(url)
346
+
347
+ if open_url:
348
+ webbrowser.open(url)
349
+
350
+
351
+ @app.command()
352
+ def hotels(
353
+ location: Annotated[str, typer.Argument(help="City, area, or hotel keyword.")],
354
+ checkin: Annotated[str, typer.Option("--checkin", help="Check-in date (YYYY-MM-DD).")],
355
+ checkout: Annotated[str, typer.Option("--checkout", help="Check-out date (YYYY-MM-DD).")],
356
+ guests: Annotated[int, typer.Option("--guests", min=1)] = 1,
357
+ rooms: Annotated[int, typer.Option("--rooms", min=1)] = 1,
358
+ open_url: Annotated[bool, typer.Option("--open", help="Open URL in browser.")] = False,
359
+ as_json: JsonOpt = False,
360
+ ):
361
+ """Generate a PointsYeah hotel search URL and optionally open it."""
362
+ import webbrowser
363
+ from pointsyeah import HotelsQuery, build_hotels_url
364
+
365
+ q = HotelsQuery(location=location, checkin=checkin, checkout=checkout,
366
+ guests=guests, rooms=rooms)
367
+ url = build_hotels_url(q)
368
+
369
+ if as_json:
370
+ import dataclasses
371
+ typer.echo(json.dumps({"command": "hotels", **dataclasses.asdict(q), "url": url, "ok": True, "error": None}))
372
+ else:
373
+ typer.echo(url)
374
+
375
+ if open_url:
376
+ webbrowser.open(url)
377
+
378
+
312
379
  # ---------------------------------------------------------------------------
313
380
  # cards — profile management (read/write user_cards.json)
314
381
  # ---------------------------------------------------------------------------
@@ -0,0 +1 @@
1
+ getfleece.io