Three issues in categoryAliasList/categoryMatcher: 1. categoryAliasList appended raw synonyms without deduplication—the addAlias helper already handles lowering and dedup, so route synonyms through it instead of direct append. 2. categoryMatcher.matches had a fast-path that returned false when the input contained no separators (-_ space), skipping the normalization step entirely. This caused legitimate matches like "frozen foods" vs "frozen" to fail when the input was a simple word that needed plural stripping to match. 3. normalizeCategory unconditionally replaced underscores/hyphens and re-joined fields even for inputs without separators. Gate the separator logic behind a ContainsAny check, and use direct slice indexing instead of TrimSuffix for the plural stripping. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
pubcli
pubcli is a Go CLI for fetching current Publix weekly ad deals by store number or ZIP code.
Features
- Fetch weekly ad deals for a specific store
- Resolve nearest Publix store from a ZIP code
- Filter deals by category, department, keyword, and BOGO status
- Sort deals by estimated savings or ending date
- List weekly categories with deal counts
- Compare nearby stores for best filtered deal coverage
- Browse deals interactively in terminal (
tui) - Output data as formatted terminal text or JSON
- Generate shell completions (
bash,zsh,fish,powershell) - Tolerate minor CLI syntax mistakes when intent is clear
- Robot-mode behavior for AI agents (compact help, auto JSON when piped, structured errors, meaningful exit codes)
Requirements
- Go
1.24.4or newer to build from source - Network access to:
https://services.publix.com/api/v4/savingshttps://services.publix.com/api/v1/storelocation
Installation
Build locally
git clone https://github.com/tayloree/publix-deals.git
cd publix-deals
go build -o pubcli ./cmd/pubcli
Install with go install
go install github.com/tayloree/publix-deals/cmd/pubcli@latest
Quick Start
Find nearby stores:
pubcli stores --zip 33101
Fetch deals using a store number:
pubcli --store 1425
Fetch deals by ZIP (uses the nearest store):
pubcli --zip 33101
Fetch JSON output:
pubcli --zip 33101 --json
Commands
pubcli
Fetch weekly ad deals.
pubcli [flags]
pubcli stores
List up to 5 nearby stores for a ZIP code.
pubcli stores --zip 33101
pubcli stores -z 32801 --json
pubcli categories
List available categories for the current week.
pubcli categories --store 1425
pubcli categories -z 33101 --json
pubcli compare
Compare nearby stores and rank them by matching deal quality.
pubcli compare --zip 33101
pubcli compare --zip 33101 --category produce --sort savings
pubcli tui
Keyboard-driven interactive browser for deal lists.
pubcli tui --zip 33101
pubcli tui --store 1425 --category meat --sort ending
Flags
Global flags:
-s, --store stringPublix store number (example:1425)-z, --zip stringZIP code for store lookup--jsonOutput JSON instead of styled terminal output
Deal filtering flags (pubcli root command):
--bogoShow only BOGO deals-c, --category stringFilter by category (example:bogo,meat,produce)-d, --department stringFilter by department (substring match, case-insensitive)-q, --query stringSearch title/description (case-insensitive)--sort stringSort byrelevance(default),savings, orending-n, --limit intLimit results (0means no limit)
Behavior Notes
- Either
--storeor--zipis required for deal and category lookups. - If only
--zipis provided, the nearest store is selected automatically. - When using text output and ZIP-based store resolution, the selected store is shown.
- Filtering is applied in this order:
bogo,category,department,query,limit. - Category matching is case-insensitive and supports practical synonyms (for example
veggiesmatchesproduce). - Department and query filters use case-insensitive substring matching.
- Running
pubcliwith no args prints compact quick-start help. - When stdout is not a TTY (for example piping to another process), JSON output is enabled automatically unless explicitly set.
CLI Input Tolerance
The CLI auto-corrects common input mistakes and prints a note: describing the normalization:
-zip 33101->--zip 33101zip=33101->--zip=33101--ziip 33101->--zip 33101stores zip 33101->stores --zip 33101
Command argument tokens are preserved for command workflows like:
pubcli completion zshpubcli help stores
JSON Output
Deals (pubcli ... --json)
Array of objects with fields:
title(string)savings(string)description(string)department(string)categories(string[])additionalDealInfo(string)brand(string)validFrom(string)validTo(string)isBogo(boolean)imageUrl(string)
Stores (pubcli stores ... --json)
Array of objects with fields:
number(string)name(string)address(string)distance(string)
Categories (pubcli categories ... --json)
Object map of category name to deal count:
{
"bogo": 175,
"meat": 88,
"produce": 81
}
Structured Errors
When command execution fails, errors include:
code(example:INVALID_ARGS,NOT_FOUND,UPSTREAM_ERROR)messagesuggestions(when available)exitCode
JSON-mode errors are emitted as:
{"error":{"code":"INVALID_ARGS","message":"unknown flag: --ziip","suggestions":["Try `--zip`.","pubcli --zip 33101"],"exitCode":2}}
Exit Codes
0success1not found2invalid arguments3upstream/network failure4internal failure
Shell Completion
Generate completions:
pubcli completion bash
pubcli completion zsh
pubcli completion fish
pubcli completion powershell
Development
Run tests:
go test ./...
Run without building:
go run ./cmd/pubcli --zip 33101 --limit 10