amazon-sync — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited amazon-sync (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
/finance-cleanupupdate_transaction, split_transaction, review_transactions, or create_category call, show the user the proposed change (current value → new value, or the full split plan with child amounts) and wait for explicit confirmation. The Phase-5 confident-fix carve-out (pure-Fresh Groceries, AMAZON PHARMACY → Healthcare, AMAZON TIPS → Groceries) still applies — those can apply without per-item confirmation but must be reported afterward./tmp/amazon-sync/ (or the alternate working dir if customized). Files there contain Order IDs, product names, amounts, and account masks. The user can opt out for debugging, but the request must be explicit somewhere in this session before the cleanup step runs.Reconcile Amazon order data with Copilot Money transactions. Fix categories, split multi-category shipments, and match card refunds. Read-only by default during analysis; writes only after user approval (with Amazon Fresh and other high-confidence fixes as the one exception — see Phase 5).
Scope: card refunds only. Amazon store-credit (gift-card balance applied to later orders) is out of scope — those mismatches get flagged, not ledgered.
Step 1 — Read the user profile first. Open the user profile at ~/.claude/copilot-money/user-profile.md. If the file doesn't exist: run mkdir -p ~/.claude/copilot-money, then copy skills/user-profile.template.md (relative to the copilot-money-mcp repo root) to the profile path. First-time bootstrap may require CWD = repo root for the template read. Note any existing ## Amazon Categorization Rules section — these rules take precedence over any inference the skill would otherwise do in Phases 5, 6, and 9. If the profile says coffee pods → Groceries, never re-derive that; apply the rule directly. If the profile contradicts a rule you were about to propose, the profile wins. Also note the general ## Preferences section for category conventions (e.g., "Coffee category is cafés only").
Step 2 — Locate the export. The user may supply Amazon data from multiple sources, each with its own CSV shape:
Your Orders/ with files named Order History.csv, Refund Details.csv, Returns Status.csv, Replacement Orders.csv, Digital Content Orders.csv.Ask for the path if not provided. Default to ~/Downloads/orders/ if a hint suggests that; otherwise ask.
Identify files by inspecting column headers, not filenames. The skill must not hard-code filenames. Use these signatures:
| Stream | Required columns (case-insensitive substring match) |
|---|---|
| Orders | order AND id; product AND (name OR title); a date column; an amount column (total, price, or unit price) |
| Refunds | order id; refund amount (or just amount); refund date (or creation date) |
| Returns | order id; return reason OR return amount; a date |
| Digital | asin; transaction amount OR price; digital order item id OR order id; a date |
For each CSV in the supplied folder, read the header row, normalize column names (lowercase, collapse spaces/underscores), and classify it into one of the streams above. Skip files you can't classify.
Map the schema once per file. Produce a column_map dict: {canonical_field: actual_column_name} with these canonical fields per stream:
order_id, order_date, ship_date, product_name, asin, unit_price, quantity, shipment_total, payment_method, carrier, shipping_charge, discountsorder_id, refund_amount, refund_date, quantity, disbursement_typeorder_id, asin, product_name, order_date, transaction_amount, subscription_order_typeWhen a field is absent, the skill degrades gracefully (e.g., no carrier → treat every shipment as non-Fresh unless the user overrides).
The unit of reconciliation is the shipment, not the order. Amazon charges per-shipment, and a single Copilot transaction usually represents one shipment. Group order rows by (order_id, ship_date) — each group is a shipment with N items.
Per-shipment data: total_amount, ship_date, payment_method, item list (each with product_name, asin, unit_price, quantity), carrier string.
Detect Amazon Fresh by scanning all carrier values in the shipment for the substring RABBIT (case-insensitive). Fresh shipments auto-categorize to Groceries with no item-level inference.
Default window: last 6 months. Extend with --all or an explicit date range.
Filter out:
ship_date is outside the window.subscription_order_type indicates a renewal — those are handled by Copilot's recurring detection, not by this skill.Save parsed shipments to /tmp/amazon-sync/shipments.json and refunds to /tmp/amazon-sync/refunds.json for the rest of the flow to consume.
Use these MCP tools:
refresh_database — ensure the local cache is current.get_transactions with query: "amazon" and the same date window. Expect >100KB responses — the MCP saves them to disk; read via jq or Python, not into context.get_accounts — map account_mask back to the payment-method suffix in Amazon data (Visa - XXXX in a shipment's payment_method → Copilot account with mask: XXXX).get_categories with view: "list" — capture the full list of user-created category IDs. Note these IDs; Plaid taxonomy IDs will not stick on writes.Save to /tmp/amazon-sync/copilot-amazon-txns.json, /tmp/amazon-sync/accounts.json, and /tmp/amazon-sync/categories.json.
For each Copilot Amazon transaction, attempt to find a matching shipment (or combo of shipments):
Match criteria (all must hold for a "confident" match):
date is in [ship_date - 2, ship_date + 5]. Tighter than you might expect — looser windows produce false positives (a $9.99 shipment 9 days before a $10 tip got spuriously matched during development).account_mask appears in the shipment's payment_method string, OR the shipment's payment is the literal string Not Available (common for Amazon Fresh).Match outcomes:
single — exactly one shipment matches. Strongest signal.single-ambig — multiple shipments match; prefer closest ship-date.combo — two or more shipments whose amounts sum to the Copilot amount (same-day bundle). Check combos up to size 5, but pre-filter candidates before the combinatorial search: only include shipments whose total_amount is less than or equal to the Copilot amount and dated within [copilot_date - 10, copilot_date] on the same account. Without this pre-filter C(N, 5) explodes on users with many unmatched shipments; with it the search is effectively O(K⁵) where K is usually under ~15.none — no match. Most common causes: export is stale (user downloaded it days ago and Copilot has newer charges), or merchant is Amazon Pharmacy / Prime subscription / tips (not in Order History). Leave these alone — do not guess.Important: never extend the date window to chase matches. False matches cause miscategorizations. "No match" is a legitimate outcome.
These classes apply without asking (Approach B rule #1):
RABBIT). Handle per the reviewed-state guard below:user_reviewed: false, any current category → set to Groceries, then review_transactions([id]).user_reviewed: true AND already Groceries → skip (no-op; nothing to change).user_reviewed: true AND not Groceries → do not auto-overwrite. The user deliberately categorized a Fresh charge as something else (e.g., Healthcare for a Fresh-delivered prescription, Household for a cleaning-supplies Fresh order). Route this transaction to Phase 6 for explicit approval with the item list attached — the user may have meant it or may want to split it per item.Never split a pure-Fresh charge — the user treats every Fresh-only shipment as groceries regardless of item mix.
Bundled charges with Fresh + non-Fresh shipments are NOT pure-Fresh — they go to Phase 6 for split approval. The bundle's Fresh shipments collapse into a single Groceries child; non-Fresh shipments each contribute a child of their own resolved category.
name contains AMAZON GROCE → Groceriesname contains AMAZON PHARMACY → Healthcarename contains AMAZON TIPS → Groceries (Fresh delivery tip)name contains AMAZON PRIME AND recurring: true → skip (Copilot's recurring handler owns it)After each write, confirm to the user what was changed. If update_transaction returns an error, report it and continue; never retry silently.
Actively scan every matched Copilot transaction — reviewed and unreviewed alike — for item-vs-category mismatches. The presence of user_reviewed: true does NOT disqualify a transaction from Phase 6; it only disqualifies it from Phase 5 auto-apply. The point of Phase 6 is to use item-level data (which Copilot never had) to surface categorizations Copilot's defaults got wrong, even on already-reviewed rows.
Four streams feed Phase 6:
Presentation format: markdown table, batches of 3–5. Columns: date, amount, current category, proposed category (for a split: a list of child amounts + categories), rationale. Always show full original_name for the Copilot txn and full product names for the items. Never truncate to normalized_merchant.
Ask in free text, not AskUserQuestion. Large batches in AskUserQuestion are slow and fatigue the user.
After the user approves a batch, apply it in Phase 7.
update_transaction(transaction_id, category_id), then review_transactions([id]).split_transaction(transaction_id, account_id, item_id, splits=[...]). All three parent IDs are required — transaction_id is the parent transaction, account_id is the parent's account ID, item_id is Copilot's Firestore item_id from the parent transaction row (not the ASIN or any Amazon item identifier). Pull all three from the parent's row in copilot-amazon-txns.json. Child amounts must sum to parent amount. Group items by resolved category first — children are one-per-category, not one-per-item. Children inherit the parent's user_reviewed state; if the parent was not reviewed, call review_transactions([child_id_1, child_id_2, ...]) after the split to mark them reviewed (there is no reviewed param on split_transaction, and update_transaction can't set reviewed either — use review_transactions).Allocation inside a single-shipment split:
unit_price * quantity as the base allocation.unit_price_tax or shipment_item_subtotal_tax), add it to that item's bucket.shipping_charge proportionally across categories by share of unit price.Allocation inside a bundled split (multiple shipments sum to one Copilot charge):
shipment_total to its resolved category's bucket. Fresh shipments all merge into the Groceries bucket.If `split_transaction` is not available in the installed MCP (older bundle), skip the split step and only do single-category updates. Tell the user: "Splits require the split_transaction tool — your MCP bundle predates it. Rebuild with `bun run pack:mcpb`, reinstall the .mcpb, and re-run." Continue with the rest of the flow.
Reversibility note: split_transaction has no reversal — undoing a split means deleting each child and restoring the parent's category. Always present the split plan for approval before executing.
For each refund in Refund Details within the window:
account_id / account_mask the refund should post back to.order_id + refund_amount to a negative-signed Copilot transaction, refund_date ± 3 days. Account check is required (same criterion as Phase 4 #3): the Copilot negative-signed transaction's account_mask must appear in the original shipment's payment_method string. Do not match a refund across accounts — a user with Amazon charges on two cards could get a false match otherwise.Returns Status if present — that has which item was returned).disbursement_type is not a card refund (e.g., Amazon balance), ignore — store credit is out of scope.After writes are applied, update ~/.claude/copilot-money/user-profile.md with rules discovered during this run. Tell the user exactly what is being saved before writing.
Amazon-specific profile section — create the section if absent:
## Amazon Categorization Rules
<!-- Auto-populated by /amazon-sync. Patterns match against product name, ASIN, carrier, seller. First match wins. -->Then append rules the user has confirmed in this session — only rules the user has confirmed, no pre-populated defaults. Examples of rule syntax (these are shapes, not rules to inject automatically):
Carrier contains "RABBIT" → Groceries (merchant/carrier-based)Merchant name contains "AMAZON PHARMACY" → Healthcare (Copilot-side merchant match)Product name contains "coffee pods" → Groceries (Amazon-side product-name substring)Seller is "Audible" → Books & Media (seller-based)Phrase each rule as a simple substring, seller-name, or field match so a reader can eyeball and edit them. Before writing, tell the user exactly what's being saved, e.g., "Adding to profile: 'Product name contains toilet paper → Household things'. OK?"
Summarize with:
ship_date in the CSV (export is stale).Then clean up PII side-files. The intermediate JSON files in /tmp/amazon-sync/ (shipments.json, refunds.json, copilot-amazon-txns.json, accounts.json, categories.json, and any match-result files) contain Order IDs, product names, amounts, account masks, and payment details. Delete the directory at end of run: rm -rf /tmp/amazon-sync/. If the user ran under an alternate working directory, delete that one instead. Mention the cleanup in the summary so the user knows it happened. Only skip cleanup if the user explicitly asks to keep the files for debugging.
user_reviewed: true means the user made a deliberate decision and auto-apply must not overwrite it. Phase 5 checks reviewed state before writing — a reviewed Fresh charge categorized as Healthcare (e.g., a prescription delivery) is routed to Phase 6 for approval, not silently converted to Groceries. Phase 6 may surface reviewed transactions for correction, but only with explicit user approval — never auto-applied.[ship - 2, ship + 5]. Looser windows produce false positives. "No match" is a valid outcome.payment_method must contain the Copilot account_mask. Do not match across accounts.update_transaction and split_transaction reject Plaid taxonomy IDs. If the needed category does not exist, create_category first.get_transactions and get_accounts routinely exceed 100KB; read via Python or jq. Do not try to pull them into context.Bash tool with Python.original_name or name, not normalized_merchant. Users need the full suffix after AMAZON MKTPL* to recall which order a charge corresponds to.ship_date is more than 3 days before the Copilot data's latest Amazon transaction, tell the user the export is stale and suggest a fresh download before acting on recent unmatched transactions.subscription_order_type is non-empty and not Not Applicable. One-off digital purchases (Kindle books bought individually, single-song MP3s, non-renewing digital rentals) flow through the normal Phase 4/6 pipeline: match on amount + date + account like retail, then categorize per profile rules (e.g., Kindle book → Books & Media; Prime Video rental → Entertainment & Experiences).shipment_total (gift-card applied), flag and leave alone. Do not try to reconstruct an Amazon balance ledger.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.