---
name: boycott-filter
description: |
  Personal boycott list — users can conversationally tell the agent which brands they
  want to avoid, and why. A local sync server exposes the list at http://127.0.0.1:7847,
  and a bundled Chrome extension warns the user when they land on pages from boycotted
  brands. Use when the user says things like "I'm done with X", "boycott Y", "never
  buying from Z again", "remove X from my boycott list", or asks to see their list.
  Trigger phrases: "boycott", "never buying from", "sick of", "add to boycott",
  "show my boycott list", "remove from boycott".
allowed-tools: Bash(curl:*)
version: 1.0.0
author: Bubble Invest <contact@bubbleinvest.com>
license: MIT
compatible-with: claude-code
tags: [boycott, consumer, shopping, brands, chrome-extension, productivity, ethical-consumption]
user-invocable: true
---

# Boycott Filter

Manage a personal boycott list conversationally. Users complain about brands, the agent adds them to a local list with their reason, and a Chrome extension warns them on any page from those brands — displaying their own words back to them.

## Overview

This skill is the conversational layer of a 3-part system:

1. **This skill** (Claude Code) — understands user intent, calls the local API
2. **Local sync server** (`scripts/server.js`, port 7847) — stores the list, serves it to the extension
3. **Chrome extension** (`extension/`) — scans pages, shows the warning banner

The user's value: they complain ONCE, they're reminded FOREVER — in their own voice, with their own reason. No more accidental clicks on brands they'd decided to stop supporting.

## Prerequisites

- Node.js 18+ installed
- Chrome (or any Manifest V3 browser) with the bundled extension loaded manually (see README)
- The local sync server must be running on port 7847

Check with:
```bash
curl -s http://127.0.0.1:7847/health
```

If not running, ask the user to run setup once in a terminal (the skill itself only has `curl` permission, by design — see README for security rationale):

```bash
bash ${CLAUDE_PLUGIN_ROOT}/scripts/setup.sh
```

Tell the user: "The boycott server isn't running. Open a terminal and run: `bash ${CLAUDE_PLUGIN_ROOT}/scripts/setup.sh` — that starts the local server and shows you how to load the Chrome extension. Then ask me again."

## Instructions

### When the user wants to ADD a brand

Detect phrases like:
- "I'm done with X"
- "Never buying from X again"
- "Boycott X"
- "Add X to my boycott list"
- "Sick of X"

Extract:
- **Brand name** (the thing they want to avoid)
- **Reason** (their own words — critical, this is what will be shown back to them)
- **Aliases** (optional — if you know subsidiaries, offer them: Nestlé → Nespresso, KitKat, Purina, etc.)

Then call:

```bash
curl -s -X POST http://127.0.0.1:7847/add \
  -H 'Content-Type: application/json' \
  -d '{"name":"BRAND","reason":"USER_REASON","aliases":["alias1","alias2"]}'
```

Keep the reason in the user's voice — don't paraphrase into corporate language. If they said "cheap garbage everywhere", store "cheap garbage everywhere" — not "low quality products with aggressive advertising".

If the user didn't give a reason, ask: "Got it. Any specific reason you want to remember why you decided to boycott?"

### When the user wants to REMOVE a brand

Detect phrases like:
- "Remove X from my boycott list"
- "Unban X"
- "Actually I'm fine with X now"

Call:
```bash
curl -s -X DELETE http://127.0.0.1:7847/remove \
  -H 'Content-Type: application/json' \
  -d '{"name":"BRAND"}'
```

### When the user wants to SEE their list

```bash
curl -s http://127.0.0.1:7847/list
```

Display the brands and reasons nicely:
- "You're currently boycotting N brands:"
- `- Temu: "Cheap garbage everywhere"`
- `- Shein: "Fast fashion, not supporting"`
- `- Nestlé (+ Nespresso, KitKat): "Water extraction in drought zones"`

### When the user asks about the extension

Explain that the Chrome extension needs to be loaded manually once:
1. Open `chrome://extensions`
2. Enable Developer mode
3. Click "Load unpacked"
4. Select: `${CLAUDE_PLUGIN_ROOT}/extension/`

After that, it auto-syncs every 30 seconds from the local server.

## Output

- **Confirmation messages** for add/remove actions — include the exact reason captured
- **List displays** grouped by company with reasons visible
- **Server status** if asked

For add, confirm like:
> "Added Temu to your boycott list. Reason: *cheap garbage, ads everywhere*. You'll see a red banner next time you land on temu.com or any page mentioning Temu."

## API reference (the local server)

| Method | Endpoint | Body | Returns |
|--------|----------|------|---------|
| GET | `/list` | — | `{"companies":[...], "updated_at":"..."}` |
| POST | `/add` | `{"name","reason","aliases":[]}` | `{"ok":true, "list":{...}}` |
| DELETE | `/remove` | `{"name"}` | `{"ok":true, "list":{...}}` |
| GET | `/health` | — | `{"status":"ok", "companies":N}` |

## Error Handling

- **Server not running** (connection refused on port 7847): run the setup script. Don't silently fail — tell the user.
- **Brand already on list**: server returns 409, tell the user gracefully ("Already on your list, reason: ...").
- **Brand not found on removal**: server returns 404, tell the user it wasn't on the list.
- **Invalid JSON**: server returns 400. Should not happen if the skill constructs the body correctly.

## Examples

**Example 1: Add with reason (most common)**

User: *"Ugh, another Shein ad. Never buying from them, fast fashion is killing everything."*

Agent action:
```bash
curl -s -X POST http://127.0.0.1:7847/add \
  -H 'Content-Type: application/json' \
  -d '{"name":"Shein","reason":"fast fashion is killing everything","aliases":["shein.com","SHEIN"]}'
```

Response: "Added Shein. Next time you land on a page mentioning them, you'll see your reason: *'fast fashion is killing everything'*."

**Example 2: Parent + subsidiaries**

User: *"Boycott Nestlé, their water extraction practices in drought zones are criminal."*

Agent action:
```bash
curl -s -X POST http://127.0.0.1:7847/add \
  -H 'Content-Type: application/json' \
  -d '{"name":"Nestlé","reason":"water extraction practices in drought zones are criminal","aliases":["Nestle","Nespresso","KitKat","Purina","Perrier","San Pellegrino"]}'
```

Response: "Added Nestlé with their main subsidiaries (Nespresso, KitKat, Purina, Perrier, San Pellegrino). The extension will match any of these. Want me to add more? Nestlé owns ~2,000 brands."

**Example 3: List view**

User: *"What's on my boycott list?"*

Agent action:
```bash
curl -s http://127.0.0.1:7847/list
```

Response formatting:
> You're currently boycotting 3 brands:
> - **Temu** — *"Cheap garbage, ads everywhere"*
> - **Shein** — *"Fast fashion is killing everything"*
> - **Nestlé** (+ Nespresso, KitKat, Purina, Perrier) — *"Water extraction practices in drought zones are criminal"*

**Example 4: Remove**

User: *"Actually remove Temu, I need something urgent from there."*

Agent action:
```bash
curl -s -X DELETE http://127.0.0.1:7847/remove \
  -H 'Content-Type: application/json' \
  -d '{"name":"Temu"}'
```

Response: "Removed Temu from your list. You won't get the warning anymore. (No judgment — but you can always add it back later.)"

## Resources

- **Server source**: `scripts/server.js` (bundled with this plugin)
- **Chrome extension**: `extension/` (bundled with this plugin, load manually once)
- **Setup script**: `scripts/setup.sh`
- **GitHub repo**: https://github.com/vdk888/boycott-filter (full source)
- **Landing page demo**: https://bubble-sentinel.netlify.app/boycott-filter.html
- **Video demo**: https://github.com/vdk888/boycott-filter/blob/main/demo.mp4
