From 6edb23cd6670724edfbada2a58354026d93f7252 Mon Sep 17 00:00:00 2001 From: null Date: Sat, 30 May 2026 17:57:34 -0500 Subject: [PATCH] chore: bump to v0.34.1.1, Claude.ai catalog seed, subscription fixes --- HISTORY.md | 10 ++++++++++ db/database.js | 28 +++++++++++++++++++++++++++- docs/top_200_us_subscriptions.csv | 2 +- package.json | 2 +- services/subscriptionService.js | 5 ++++- tests/subscriptionService.test.js | 18 ++++++++++++++++++ 6 files changed, 61 insertions(+), 4 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index b413e13..03865bb 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,14 @@ # Bill Tracker โ€” Changelog +## v0.34.1.1 + +### ๐Ÿ”ง Changed + +- **Bump** โ€” `0.34.1` โ†’ `0.34.1.1` +- **Claude.ai catalog seed** โ€” Updated subscription catalog to match Claude Pro transaction descriptions. + +--- + ## v0.34.1 ### ๐Ÿš€ Features @@ -7,6 +16,7 @@ - **Persistent tracker bill ordering** โ€” Added `sort_order` on bills, `PUT /api/bills/reorder`, and tracker drag/up/down controls so bill order can be changed and remembered. - **Bill archive endpoint** โ€” Added `PUT /api/bills/:id/archived` to hide or restore bills without deleting them. - **Subscription catalog matching** โ€” Subscription recommendations now use the DB-backed `subscription_catalog` as a strong matching signal alongside the existing recurrence algorithm. Known services can surface as high-confidence recommendations, with catalog name/type/website carried into the Track flow. +- **Claude.ai catalog seed** โ€” Updated the known subscription catalog so Claude.ai/Anthropic transaction descriptors match the Claude Pro subscription entry. - **Subscription transaction match search** โ€” Added `/api/subscriptions/transaction-matches` for the Subscriptions page. Bank transaction search now annotates known catalog hits, shows "Known: service" badges, and pre-fills new subscriptions from catalog metadata when available. - **Payoff Simulator page** โ€” New `/payoff` route in sidebar. Select any debt from a dropdown; inputs auto-populate from bill rate, minimum, and expected amount (all editable). Live-updating custom SVG chart with 3 tracks: slate dashed (min-only), indigo dashed (snowball plan), amber solid (simulation). Stats cards show interest saved vs minimum, time saved, and total paid breakdown. "Apply to budget" pushes sim payment back to bill's expected amount with undo support. - **Snowball plan lifecycle** โ€” Snowball page now supports committing to a plan. "Start Snowball Plan" button appears once โ‰ฅ3 readiness items are checked. Active plan shows a collapsible emerald banner with pulsing status dot, per-debt progress bars, and on-track/ahead/behind indicators computed from the plan's initial snapshot vs. current balances. Actions: Pause ยท Resume ยท Complete ยท Abandon ยท New Plan (with AlertDialog confirmation). diff --git a/db/database.js b/db/database.js index 091465f..5d590f7 100644 --- a/db/database.js +++ b/db/database.js @@ -167,7 +167,7 @@ const SUBSCRIPTION_CATALOG_ROWS = [ [91,'Todoist','Software & Productivity','software','https://todoist.com/pricing','todoist.com'], [92,'Grammarly','Writing & AI','software','https://www.grammarly.com/plans','grammarly.com'], [93,'ChatGPT','AI','software','https://chatgpt.com/pricing','chatgpt.com'], - [94,'Claude','AI','software','https://claude.ai/upgrade','claude.ai'], + [94,'Claude.ai','AI','software','https://claude.ai/upgrade','anthropic.com'], [95,'Perplexity','AI','software','https://www.perplexity.ai/pro','perplexity.ai'], [96,'Gemini Advanced','AI','software','https://one.google.com/about/google-ai-plans/','one.google.com'], [97,'GitHub Copilot','Developer Tools','software','https://github.com/features/copilot/plans','github.com'], @@ -2472,6 +2472,32 @@ function runMigrations() { `); db.exec('CREATE INDEX IF NOT EXISTS idx_snowball_plans_user ON snowball_plans(user_id, status, created_at)'); } + }, + { + version: 'v0.74', + description: 'subscription_catalog: Claude.ai Anthropic matching', + dependsOn: ['v0.73'], + run: function() { + db.prepare(` + UPDATE subscription_catalog + SET name = 'Claude.ai', + category = 'AI', + subscription_type = 'software', + website = 'https://claude.ai/upgrade', + domain = 'anthropic.com' + WHERE name IN ('Claude', 'Claude.ai') + OR domain IN ('claude.ai', 'anthropic.com') + `).run(); + + db.prepare(` + INSERT INTO subscription_catalog (rank, name, category, subscription_type, website, domain) + SELECT 94, 'Claude.ai', 'AI', 'software', 'https://claude.ai/upgrade', 'anthropic.com' + WHERE NOT EXISTS ( + SELECT 1 FROM subscription_catalog + WHERE name = 'Claude.ai' OR domain IN ('claude.ai', 'anthropic.com') + ) + `).run(); + } } ]; diff --git a/docs/top_200_us_subscriptions.csv b/docs/top_200_us_subscriptions.csv index 0d5ef20..33e9167 100644 --- a/docs/top_200_us_subscriptions.csv +++ b/docs/top_200_us_subscriptions.csv @@ -92,7 +92,7 @@ Rank,Service,Category,Subcategory,Subscription_or_Plan,US_Availability,Website,S 91,Todoist,Software & Productivity,Task management,Todoist Pro,United States,https://todoist.com/pricing,https://todoist.com/pricing,Major U.S. consumer/professional software subscriptions; app revenue and SaaS prominence where applicable; curated 92,Grammarly,Writing & AI,Writing assistant,Grammarly Pro,United States,https://www.grammarly.com/plans,https://www.grammarly.com/plans,Major U.S. consumer/professional software subscriptions; app revenue and SaaS prominence where applicable; curated 93,ChatGPT,AI,AI assistant,ChatGPT Plus / Pro,United States,https://chatgpt.com/pricing,https://chatgpt.com/pricing,Major U.S. consumer/professional software subscriptions; app revenue and SaaS prominence where applicable; curated -94,Claude,AI,AI assistant,Claude Pro,United States,https://claude.ai/upgrade,https://claude.ai/upgrade,Major U.S. consumer/professional software subscriptions; app revenue and SaaS prominence where applicable; curated +94,Claude.ai,AI,AI assistant,Claude Pro,United States,https://claude.ai/upgrade,https://claude.ai/upgrade,Major U.S. consumer/professional software subscriptions; app revenue and SaaS prominence where applicable; curated 95,Perplexity,AI,AI search,Perplexity Pro,United States,https://www.perplexity.ai/pro,https://www.perplexity.ai/pro,Major U.S. consumer/professional software subscriptions; app revenue and SaaS prominence where applicable; curated 96,Gemini Advanced,AI,AI assistant / Google One AI,Google AI plans,United States,https://one.google.com/about/google-ai-plans/,https://one.google.com/about/google-ai-plans/,Major U.S. consumer/professional software subscriptions; app revenue and SaaS prominence where applicable; curated 97,GitHub Copilot,Developer Tools,AI coding,GitHub Copilot,United States,https://github.com/features/copilot/plans,https://github.com/features/copilot/plans,Major U.S. consumer/professional software subscriptions; app revenue and SaaS prominence where applicable; curated diff --git a/package.json b/package.json index 9d312c5..b91ac9d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bill-tracker", - "version": "0.34.1", + "version": "0.34.1.1", "description": "Monthly bill tracking system", "main": "server.js", "scripts": { diff --git a/services/subscriptionService.js b/services/subscriptionService.js index cda4636..2ed452a 100644 --- a/services/subscriptionService.js +++ b/services/subscriptionService.js @@ -82,6 +82,7 @@ function catalogDomainKeys(entry) { if (labels.length >= 2) { keys.add(labels.join(' ')); keys.add(labels.slice(-2).join(' ')); + if (labels[0].length >= 5) keys.add(labels[0]); } } return [...keys].filter(key => key.length >= 4); @@ -498,8 +499,10 @@ function createSubscriptionFromRecommendation(db, userId, payload = {}) { expected_amount: payload.expected_amount, billing_cycle: billingCycleForCycleType(payload.cycle_type || 'monthly'), cycle_type: payload.cycle_type || 'monthly', - cycle_day: payload.cycle_type === 'annual' || payload.cycle_type === 'quarterly' + cycle_day: (payload.cycle_type === 'annual' || payload.cycle_type === 'quarterly') ? String(new Date(`${seenDate}T00:00:00`).getMonth() + 1) + : (payload.cycle_type === 'weekly' || payload.cycle_type === 'biweekly') + ? 'monday' : String(payload.due_day || 1), is_subscription: 1, subscription_type: SUBSCRIPTION_TYPES.includes(payload.subscription_type) ? payload.subscription_type : 'other', diff --git a/tests/subscriptionService.test.js b/tests/subscriptionService.test.js index 772fe20..a926dff 100644 --- a/tests/subscriptionService.test.js +++ b/tests/subscriptionService.test.js @@ -71,3 +71,21 @@ test('subscription transaction search annotates known catalog matches', () => { assert.equal(match.catalog_match.name, 'Netflix'); assert.equal(match.catalog_match.subscription_type, 'streaming'); }); + +test('Claude.ai catalog seed matches Anthropic transaction descriptors', () => { + const db = getDb(); + const userId = createUser(db, 'claude'); + const transactionId = createTransaction(db, userId, { + description: 'ANTHROPIC CLAUDE PRO', + payee: 'ANTHROPIC', + amount: -2000, + }); + + const matches = searchSubscriptionTransactions(db, userId, { q: 'anthropic', limit: 10 }); + const match = matches.find(item => item.id === transactionId); + + assert.ok(match, 'Anthropic transaction should be returned by subscription search'); + assert.equal(match.is_known_subscription, true); + assert.equal(match.catalog_match.name, 'Claude.ai'); + assert.equal(match.catalog_match.subscription_type, 'software'); +});