Skill: chipagosfinest/just-fucking-cancel (version 1.2.0)

--- name: just-fucking-cancel version: 1.2.0 description: Find and cancel unwanted subscriptions by analyzing bank transactions. Detects recurring charges, calculates annual waste, and provides cancel URLs. CSV-based analysis with optional Plaid integration for ClawdBot users. author: ClawdBot attribution: Originally created by rohunvora (https://github.com/rohunvora/just-fucking-cancel) category: finance tags: - subscriptions - cancel - money-saving - finance - budgeting - recurring-charges - personal-finance env: - name: PLAID_CLIENT_ID description: Plaid client ID for transaction access (optional - only needed for Plaid integration) required: false - name: PLAID_SECRET description: Plaid secret key (optional - only needed for Plaid integration) required: false - name: PLAID_ACCESS_TOKEN description: User's Plaid access token for their connected bank account (optional) required: false triggers: - cancel subscriptions - audit subscriptions - find recurring charges - what am I paying for - subscription cleanup - save money --- # Just Fucking Cancel Analyze transactions, categorize subscriptions, generate HTML audit, help cancel. ## How It Works This skill analyzes your transaction history to find recurring charges (subscriptions), helps you categorize them, and provides direct cancel URLs. **No automated cancellation** - you control every action. ``` Transaction Data → Pattern Detection → User Categorization → HTML Audit → Cancel URLs ``` ## Triggers - "cancel subscriptions", "audit subscriptions" - "find recurring charges", "what am I paying for" - "subscription audit", "clean up subscriptions" ## Data Sources ### Option A: CSV Upload (Recommended - Fully Local) Upload a transaction CSV from your bank. **All processing happens locally** - no data leaves your machine. Supported formats: - **Apple Card**: Wallet → Card Balance → Export - **Chase**: Accounts → Download activity → CSV - **Amex**: Statements & Activity → Download → CSV - **Citi**: Account Details → Download Transactions - **Bank of America**: Activity → Download → CSV - **Capital One**: Transactions → Download - **Mint / Copilot**: Transactions → Export ### Option B: Plaid Integration (ClawdBot Only) If you have ClawdBot with Plaid connected, transactions can be pulled automatically. **Important**: This requires Plaid credentials and sends data to Plaid's servers: - `PLAID_CLIENT_ID` - Your Plaid client ID - `PLAID_SECRET` - Your Plaid secret key - `PLAID_ACCESS_TOKEN` - Access token for the bank connection **Privacy note**: When using Plaid, transaction data is transmitted to Plaid's API. CSV analysis is fully local. ## Workflow ### 1. Get Transactions - CSV: User uploads file, analyzed locally - Plaid: Pull last 6-12 months via API (requires credentials) ### 2. Analyze Recurring Charges - Detect same merchant, similar amounts, monthly/annual patterns - Flag subscription-like charges (streaming, SaaS, memberships) - Calculate charge frequency and total annual cost ### 3. Categorize with User For each subscription, ask user to categorize: - **Cancel** - Stop immediately - **Investigate** - Needs decision (unsure, trapped in contract) - **Keep** - Intentional, continue paying Ask in batches of 5-10 to avoid overwhelming. ### 4. Generate HTML Audit Create interactive HTML report with: - Summary: subscriptions found, total waste, potential savings - Sections: Cancelled / Needs Decision / Keeping - Privacy toggle to blur service names - Dark mode support ### 5. Provide Cancel URLs For each service to cancel: 1. Look up direct cancel URL from [common-services.md](references/common-services.md) 2. Provide URL to user - **user navigates manually** 3. Include dark pattern warnings and tips **No automated browser interaction** - this skill provides URLs and guidance only. You control the actual cancellation. ## HTML Structure Three sections, auto-hide when empty: - **Cancelled** (green badge, strikethrough) - Done items - **Needs Decision** (orange badge) - Has checkboxes for selection - **Keeping** (grey badge) - Reference only Features: - Floating copy button for selected items - Privacy toggle blurs service names - Collapsible sections - Dark mode support ## Cancellation Tips See [common-services.md](references/common-services.md) for: - Direct cancel URLs for 50+ services - Dark pattern warnings (gym contracts, phone-only) - Retention script responses - Credit card dispute backup ## Privacy Summary | Data Source | Where Processed | Data Leaves Device? | |-------------|-----------------|---------------------| | CSV Upload | Local only | No | | Plaid API | Plaid servers | Yes (to Plaid) | ## Related - `plaid` - Bank account connection - `ynab` - Budget tracking - `copilot` - Financial insights

path: PUBLISH.md
content: # Publishing just-fucking-cancel to ClawdHub

## Attribution
- **Original**: https://github.com/rohunvora/just-fucking-cancel by @rohunvora
- **Adapted for ClawdBot**: @chipagosfinest
- **Mostly written by**: Claude (Anthropic)

## Publish Command

```bash
# 1. Install ClawdHub CLI (if not already)
npm i -g clawdhub

# 2. Login to ClawdHub
clawdhub login

# 3. Publish the skill
cd /path/to/clawdbot-railway
clawdhub publish ./skills/just-fucking-cancel \
  --slug just-fucking-cancel \
  --name "just-fucking-cancel" \
  --version 1.0.0 \
  --changelog "Initial release - subscription audit and cancellation skill.

Originally created by rohunvora (https://github.com/rohunvora/just-fucking-cancel).
Adapted for ClawdBot by @chipagosfinest.
Mostly written by Claude.

Features:
- Analyze bank CSV exports to find recurring charges
- Interactive categorization (Cancel/Investigate/Keep)
- HTML audit report with privacy toggle
- Browser automation for cancellations
- 50+ common service cancel URLs

DM @chipagosfinest on X if you need anything."
```

## After Publishing

The skill will be available at:
```
https://clawdhub.com/chipagosfinest/just-fucking-cancel
```

Add to any clawdbot.json:
```json
"just-fucking-cancel": {
  "location": "https://clawdhub.com/chipagosfinest/just-fucking-cancel"
}
```

Or install via CLI:
```bash
clawdhub install chipagosfinest/just-fucking-cancel
```


path: assets/template.html
content: <!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>just-fucking-cancel</title>
  <style>
    :root {
      --bg: #fff;
      --text: #222;
      --muted: #666;
      --border: #ddd;
      --accent: #1976d2;
      --pending: #fff3cd;
      --pending-border: #ffc107;
      --cancelled: #e8e8e8;
      --investigate: #f57c00;
      --keep: #388e3c;
    }
    @media (prefers-color-scheme: dark) {
      :root {
        --bg: #1a1a1a;
        --text: #e0e0e0;
        --muted: #999;
        --border: #333;
        --accent: #64b5f6;
        --pending: #3d3200;
        --pending-border: #ffc107;
        --cancelled: #2a2a2a;
        --investigate: #ffb74d;
        --keep: #81c784;
      }
    }
    body {
      max-width: 900px;
      margin: 40px auto;
      padding: 0 20px;
      font-family: system-ui;
      background: var(--bg);
      color: var(--text);
    }

    /* SHARE CARD - Receipt style */
    .share-card {
      background: transparent;
      color: var(--text);
      padding: 1.5rem 0;
      margin: 0 0 2rem 0;
      text-align: left;
      border-bottom: 2px dashed var(--accent);
    }
    .share-card .scope {
      font-size: 0.875rem;
      color: var(--accent);
      margin-bottom: 0.75rem;
    }
    .share-card .breakdown {
      font-size: 1.1rem;
      color: var(--accent);
      margin-bottom: 0.5rem;
    }
    .share-card h1 {
      font-size: 2.75rem;
      font-weight: 800;
      margin: 0;
      letter-spacing: -1px;
      color: var(--text);
    }
    .share-card h1 span {
      font-size: 1.25rem;
      font-weight: 500;
      color: var(--muted);
    }
    .share-card .timestamp {
      font-size: 0.75rem;
      color: var(--muted);
      margin-top: 1rem;
    }

    /* Rest of page header */
    .page-meta {
      color: var(--muted);
      font-size: 0.875rem;
      margin-bottom: 2rem;
      text-align: center;
    }

    /* Sections */
    .section { margin: 2rem 0; }
    .section.empty { display: none; }
    .section-header {
      display: flex;
      align-items: center;
      gap: 0.5rem;
      margin-bottom: 1rem;
      padding-bottom: 0.5rem;
      border-bottom: 2px solid var(--border);
      cursor: pointer;
    }
    .section-header::before {
      content: '\25BC';
      font-size: 0.6rem;
      color: var(--muted);
      transition: transform 0.2s;
    }
    .section.collapsed .section-header::before {
      transform: rotate(-90deg);
    }
    .section.collapsed table,
    .section.collapsed details {
      display: none;
    }
    .section-header h2 { margin: 0; font-size: 1.25rem; }
    .section-count {
      margin-left: auto;
      color: var(--muted);
      font-size: 0.875rem;
    }

    /* Badges */
    .badge {
      padding: 4px 12px;
      border-radius: 4px;
      font-size: 0.7rem;
      font-weight: bold;
      text-transform: uppercase;
      letter-spacing: 0.5px;
    }
    .badge-pending { background: var(--pending-border); color: #000; }
    .badge-cancelled { background: var(--keep); color: white; }
    .badge-investigate { background: var(--investigate); color: white; }
    .badge-keep { background: #666; color: white; }

    /* Table */
    table { border-collapse: collapse; width: 100%; }
    td, th { border: 1px solid var(--border); padding: 10px 12px; text-align: left; }
    th { background: var(--border); font-size: 0.85rem; }
    .amount { font-family: monospace; text-align: right; white-space: nowrap; }
    .note { font-size: 0.85rem; color: var(--muted); }
    .checkbox { width: 30px; text-align: center; }
    input[type="checkbox"] { width: 18px; height: 18px; cursor: pointer; }

    /* ROW STATES */
    tr.pending {
      background: var(--pending);
    }
    tr.pending td:first-child {
      border-left: 4px solid var(--pending-border);
    }
    tr.cancelled {
      background: var(--cancelled);
      color: var(--muted);
    }
    tr.cancelled td {
      text-decoration: line-through;
      text-decoration-color: var(--muted);
    }
    tr.cancelled .status-cell {
      text-decoration: none;
      font-style: italic;
    }
    tr.investigate td:first-child { border-left: 4px solid var(--investigate); }
    tr.keep td:first-child { border-left: 4px solid var(--keep); }

    /* Category headers in tables */
    .category-header {
      background: var(--border);
      font-weight: bold;
      color: var(--muted);
      font-size: 0.8rem;
      text-transform: uppercase;
      letter-spacing: 0.5px;
    }
    .category-header td { border-left: none !important; }

    /* Details/expand sections */
    details { margin-top: 1rem; }
    summary { cursor: pointer; font-weight: 600; }
    summary:hover { color: var(--accent); }

    /* Privacy toggle */
    .privacy-toggle {
      margin-left: 1rem;
      padding: 4px 10px;
      font-size: 0.75rem;
      background: var(--border);
      border: none;
      border-radius: 4px;
      cursor: pointer;
      color: var(--muted);
    }
    .privacy-toggle:hover { background: var(--accent); color: white; }
    body.privacy-mode .service-name {
      filter: blur(5px);
      user-select: none;
    }
    body.privacy-mode .service-name:hover {
      filter: none;
    }

    /* Floating copy button */
    .floating-copy {
      position: fixed;
      bottom: 20px;
      right: 20px;
      background: var(--text);
      color: var(--bg);
      border: none;
      padding: 12px 20px;
      border-radius: 8px;
      font-size: 0.9rem;
      font-weight: 600;
      cursor: pointer;
      display: none;
      box-shadow: 0 2px 10px rgba(0,0,0,0.2);
      z-index: 1000;
    }
    .floating-copy:hover { opacity: 0.9; }
    .floating-copy.visible { display: block; }
    .floating-copy.copied { background: var(--keep); }
  </style>
</head>
<body>

<!-- SHARE CARD - Update after analysis -->
<div class="share-card">
  <p class="scope">found N subscriptions · N transactions</p>
  <p class="breakdown">Cancelled N · Keeping N</p>
  <h1>just saved $XX,XXX/yr <span>($X,XXX/mo)</span></h1>
  <p class="timestamp">last run DATE at TIME</p>
</div>

<p class="page-meta">
  Subscription audit
  <button class="privacy-toggle" onclick="togglePrivacy()">Hide names</button>
</p>

<!-- CANCELLED - Done, strikethrough -->
<div class="section" id="section-cancelled">
  <div class="section-header" onclick="this.parentElement.classList.toggle('collapsed')">
    <span class="badge badge-cancelled">Done</span>
    <h2>Cancel</h2>
    <span class="section-count" data-section="cancelled"></span>
  </div>
  <table>
    <tr>
      <th style="width:30px"></th>
      <th>Service</th>
      <th>Monthly</th>
      <th>Status</th>
    </tr>
    <!-- TEMPLATE: Cancelled items
    <tr class="cancelled" data-status="cancelled">
      <td>✓</td>
      <td><strong class="service-name">Service Name</strong></td>
      <td class="amount">$XX</td>
      <td class="status-cell">Cancelled DATE</td>
    </tr>
    -->
  </table>
</div>

<!-- INVESTIGATE - Needs decision -->
<div class="section" id="section-investigate">
  <div class="section-header" onclick="this.parentElement.classList.toggle('collapsed')">
    <span class="badge badge-investigate">Decide</span>
    <h2>Needs Decision</h2>
    <span class="section-count" data-section="investigate"></span>
  </div>
  <table>
    <tr>
      <th class="checkbox">Cancel?</th>
      <th>Service</th>
      <th>Monthly</th>
      <th>Issue</th>
    </tr>
    <!-- TEMPLATE: Items needing decision
    <tr class="investigate" data-status="investigate">
      <td class="checkbox"><input type="checkbox"></td>
      <td><strong class="service-name">Service Name</strong></td>
      <td class="amount">$XX</td>
      <td class="note">Why this needs review</td>
    </tr>
    -->
  </table>
</div>

<!-- KEEPING - No checkbox needed -->
<div class="section" id="section-keep">
  <div class="section-header" onclick="this.parentElement.classList.toggle('collapsed')">
    <span class="badge badge-keep">Keep</span>
    <h2>Keeping</h2>
    <span class="section-count" data-section="keep"></span>
  </div>
  <table>
    <tr>
      <th>Service</th>
      <th>Monthly</th>
      <th>Reason</th>
    </tr>
    <!-- TEMPLATE: Category header (optional)
    <tr class="category-header"><td colspan="3">Category Name</td></tr>
    -->
    <!-- TEMPLATE: Keep items
    <tr class="keep" data-status="keep">
      <td><strong class="service-name">Service Name</strong></td>
      <td class="amount">$XX</td>
      <td class="note">Why keeping</td>
    </tr>
    -->
  </table>
</div>

<!-- Floating copy button -->
<button class="floating-copy" onclick="copyChecked()">Copy selected</button>

<script>
function togglePrivacy() {
  document.body.classList.toggle('privacy-mode');
  const btn = document.querySelector('.privacy-toggle');
  btn.textContent = document.body.classList.contains('privacy-mode') ? 'Show names' : 'Hide names';
}

function updateCounts() {
  document.querySelectorAll('[data-section]').forEach(el => {
    const section = el.dataset.section;
    const count = document.querySelectorAll(`[data-status="${section}"]`).length;
    el.textContent = count > 0 ? `${count} items` : '';
  });
}

function updateFloatingButton() {
  const checked = document.querySelectorAll('input[type="checkbox"]:checked');
  const btn = document.querySelector('.floating-copy');
  btn.classList.toggle('visible', checked.length > 0);
  btn.textContent = checked.length > 0 ? `Copy ${checked.length} selected` : 'Copy selected';
}

function copyChecked() {
  const checked = document.querySelectorAll('input[type="checkbox"]:checked');
  const items = [];
  checked.forEach(cb => {
    const row = cb.closest('tr');
    const name = row.querySelector('.service-name')?.textContent || '';
    const amount = row.querySelector('.amount')?.textContent || '';
    if (name) items.push(`${name} (${amount})`);
  });

  const text = `Cancel these: ${items.join(', ')}`;
  navigator.clipboard.writeText(text).then(() => {
    const btn = document.querySelector('.floating-copy');
    btn.classList.add('copied');
    btn.textContent = 'Copied!';
    setTimeout(() => {
      btn.classList.remove('copied');
      updateFloatingButton();
    }, 1500);
  });
}

document.querySelectorAll('input[type="checkbox"]').forEach(cb => {
  cb.addEventListener('change', function() {
    const row = this.closest('tr');
    if (this.checked) {
      row.classList.add('pending');
      row.dataset.status = 'pending';
    } else {
      row.classList.remove('pending');
      const section = row.closest('.section').id.replace('section-', '');
      row.dataset.status = section;
    }
    updateCounts();
    updateFloatingButton();
    hideEmptySections();
  });
});

function hideEmptySections() {
  document.querySelectorAll('.section').forEach(section => {
    const rows = section.querySelectorAll('tr[data-status]');
    section.classList.toggle('empty', rows.length === 0);
  });
}

updateCounts();
updateFloatingButton();
hideEmptySections();
</script>
</body>
</html>


path: references/common-services.md
content: # Common Services - Cancellation Guide

Quick reference for cancellation URLs, dark patterns, and tips.

## Streaming & Entertainment

| Service | Cancel URL | Notes |
|---------|-----------|-------|
| Netflix | netflix.com/cancelplan | Easy, immediate |
| Spotify | spotify.com/account | Look for "Change plan" |
| YouTube Premium | youtube.com/paid_memberships | Under subscriptions |
| YouTube TV | tv.youtube.com/settings | Must pause first or cancel |
| Hulu | account.hulu.com | Look for "Cancel" |
| Disney+ | disneyplus.com/account | Under subscription |
| HBO Max | account.hbomax.com | Depends on billing source |
| Apple TV+ | appleid.apple.com | Under subscriptions |
| Amazon Prime | amazon.com/primecentral | They push retention offers |
| Paramount+ | paramountplus.com/account | Easy |
| Peacock | peacocktv.com/account | Under plan details |

## AI & Developer Tools

| Service | Cancel URL | Notes |
|---------|-----------|-------|
| ChatGPT Plus | chat.openai.com/settings | Under subscription |
| Claude Pro | claude.ai/settings | Under subscription |
| Midjourney | midjourney.com/account | Under Manage Sub |
| GitHub Copilot | github.com/settings/copilot | Under subscription |
| Replit | replit.com/account | Under subscription |
| Cursor | cursor.sh/settings | Under subscription |
| ElevenLabs | elevenlabs.io/subscription | Under your profile |
| Railway | railway.app/account/billing | Check usage first |
| Vercel | vercel.com/account | Team vs personal |

## Productivity

| Service | Cancel URL | Notes |
|---------|-----------|-------|
| Notion | notion.so/my-account | Under Plans |
| Superhuman | superhuman.com/settings | Email required |
| Dropbox | dropbox.com/account/plan | Retention offers |
| 1Password | 1password.com/account | Check family vs individual |
| Todoist | todoist.com/app/settings/subscription | Easy |
| Evernote | evernote.com/account | Dark patterns |

## Social & Communication

| Service | Cancel URL | Notes |
|---------|-----------|-------|
| X Premium | twitter.com/settings/subscriptions | Under monetization |
| Discord Nitro | discord.com/settings/subscriptions | Easy |
| LinkedIn Premium | linkedin.com/premium/cancel | Heavy retention |
| Slack | slack.com/account/settings | Workspace admin only |

## News & Media

| Service | Cancel URL | Notes |
|---------|-----------|-------|
| NYTimes | nytimes.com/subscription | Call required for some plans |
| WSJ | wsj.com/account | Call may be required |
| Washington Post | washingtonpost.com/my-account | Under subscription |
| The Athletic | theathletic.com/account | Under membership |
| Substack | substack.com/account/settings | Per-publication |

## Fitness (Dark Pattern Warning)

| Service | How to Cancel | Warning |
|---------|--------------|---------|
| **Equinox** | In-person or certified mail | 45-day notice, contract trap |
| **24 Hour Fitness** | Call 1-800-432-6348 | Retention scripts, be firm |
| **Planet Fitness** | In-person at home club | Can't cancel online |
| **LA Fitness** | In-person or certified mail | 30-day notice |
| **Anytime Fitness** | Varies by franchise | Check your contract |
| **ClassPass** | classpass.com/account | Credits expire |
| **Peloton** | onepeloton.com/membership | App vs equipment |

## Dark Pattern Services

These make cancellation intentionally difficult:

### Phone-Only Cancellation
- **SiriusXM**: 1-866-635-2349 (heavy retention)
- **NYTimes** (some plans): 1-800-591-9233
- **Gym memberships**: See fitness section above

### Certified Mail Required
- **Equinox**: Certified mail to home club
- **LA Fitness**: Certified mail with 30-day notice

### Hidden Cancel Buttons
- **Amazon Prime**: 4+ clicks deep, multiple "are you sure" screens
- **Adobe Creative Cloud**: Chat required for annual plans

### Retention Offer Pressure
- **SiriusXM**: Will offer 50%+ discounts
- **Cable/Internet**: Retention department has better deals
- **LinkedIn Premium**: Multiple guilt screens

## Tips for Difficult Cancellations

### Gym Contracts
1. **Check end date** - May be approaching natural end
2. **Move clause** - Cancellable if moving 25+ miles away
3. **Medical exemption** - Doctor's note for injury
4. **Transfer option** - Some contracts allow member transfer
5. **Credit card** - Dispute if they won't cancel

### Retention Scripts
Say: "I've already made my decision. Please process the cancellation."
Repeat as needed. Don't engage with offers.

### Documentation
- Screenshot confirmation pages
- Save cancellation emails
- Note date/time of calls
- Get confirmation numbers

### Credit Card Backup
If a service won't cancel:
1. Request cancellation in writing (email)
2. Screenshot the request
3. If they keep charging, dispute with credit card as "cancelled service"


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (MEDIUM)