---
name: tag-sync
description: "This skill should be used when the user asks to 'manage tag vocabulary', 'update raindrop tags', 'sync tag vocabulary', 'curate tags', 'refresh tags file', 'rebuild tag vocabulary', 'what tags should I use', 'tag reference', 'raindrop tag vocabulary', 'create tags reference', 'tag inventory', 'tag sync'. Fetches the user's Raindrop tags, selects the top N by usage count, adds one-line characterizations, groups them by cluster, and writes or syncs the vocabulary file at ~/.claude/references/raindrop-tags.md."
user-invocable: true
argument-hint: "[count|--reset]"
allowed-tools:
  - Read
  - Write
  - mcp__raindrop__find_tags
  - mcp__raindrop__find_bookmarks
---

# Tag Sync

Fetch tags from Raindrop, curate the top N by usage, characterize each with
a one-line description, and write or sync the vocabulary file. Follows the
vendor-sync pattern: registry (vocabulary file) -> fetch from external
(Raindrop API) -> diff against local -> preview -> apply after approval.

The output file at `~/.claude/references/raindrop-tags.md` is consumed by
`/session-bookmarks` when selecting tags for new bookmarks.

## Arguments

- `/tag-sync` — sync existing vocabulary (or create if no file exists)
- `/tag-sync 100` — set tag count to 100, then sync/create
- `/tag-sync --reset` — force full recreation (re-characterize + re-cluster all)

## Edge Cases

- **Raindrop MCP unavailable** — report error and exit. Do not write a partial file.
- **`find_tags` returns fewer than `tag_count`** — use all available tags. Store
  the actual count in frontmatter so the next sync has a realistic target.
- **`ai-bookmarked` not in top N** — always inject it regardless of rank. It is
  listed in `mandatory_tags` frontmatter.
- **No bookmarks found for a tag** (characterization step) — infer from tag name
  alone. Flag with `*` in preview: `*auto-inferred from name`.
- **`~/.claude/references/` directory missing** — create it before writing.
- **Vocabulary fetched today, no explicit args** — warn: "Vocabulary was
  already synced today. Pass a count or `--reset` to force." Do not re-fetch.
- **User declines all changes** — report "No changes made" and exit.

## Workflow

### Step 1: Parse arguments

Accept an optional integer argument or `--reset` flag.

- Integer sets `tag_count` (how many tags to include).
- `--reset` forces creation mode even if the file exists.
- No argument: use `tag_count` from existing file, or default 75.

### Step 2: Load existing vocabulary

```
Read(file_path="~/.claude/references/raindrop-tags.md")
```

- **File exists** — enter **sync mode**. Extract `tag_count`, `fetched_at`,
  and existing tag entries (tag name, count, characterization, cluster).
  Also extract optional config fields from frontmatter: `blocklist`,
  `context_tags`, `conventions`. **Preserve these unchanged through the
  sync cycle** — they are user-authored config, not Raindrop-derived data.
  Check staleness: if `fetched_at` matches today's date and no explicit
  arguments were passed, warn and suggest `--reset` or a count argument.
- **File missing** (or `--reset`) — enter **creation mode**. Seed default
  config fields: `blocklist: ["5", "4", "3", "2", "imported", "toread",
  "unread", "for:*"]`, `context_tags: []`, `conventions: []`.

### Step 3: Fetch tags from Raindrop

```
mcp__raindrop__find_tags()
```

Returns tags with usage counts. Paginate if `has_more=true`. Sort by count
descending. Take the top `tag_count` entries. Always include `ai-bookmarked`
in the target set regardless of rank.

### Step 4: Diff (sync mode only)

Compare target set against existing vocabulary:

| Category | Action |
|----------|--------|
| **New tags** (in Raindrop, not in file) | Must characterize (Step 5) |
| **Dropped tags** (in file, below threshold now) | Candidate to remove |
| **Retained tags** (in both) | Keep characterization, update count |
| **Count-only changes** | Apply silently, no user action needed |

If no new tags and no dropped tags (only count changes), report "Tag
vocabulary is in sync — only usage counts updated" and offer to apply
count updates in place.

### Step 5: Characterize new tags

For each new tag (creation mode: all tags; sync mode: only new arrivals),
sample 2-3 bookmarks to ground the description:

```
mcp__raindrop__find_bookmarks(has_tags=["<tag-name>"], limit=3, sort="random")
```

From bookmark titles and excerpts, write a concise one-line characterization
(10-15 words max). If `find_bookmarks` returns empty, infer from the tag
name (e.g., `conways-law` -> "Org structure effects on system architecture").

Do NOT call `find_bookmarks` for tags that already have characterizations
in the existing file (sync mode).

### Step 6: Assign clusters

Four groups:

| Cluster | What goes here |
|---------|---------------|
| **Ecosystem** | Language, framework, platform, tool names |
| **Architecture** | Design patterns, methodology, structural concepts |
| **Content Type** | Article genre, reference type, quality tier |
| **Other** | Anything that doesn't clearly fit above |

- **Retained tags** keep their existing cluster (stability across syncs).
- **New tags** are auto-assigned from name + sample bookmark content.
- `--reset` forces re-assignment of all clusters.

### Step 7: Preview

Show the proposed state before writing.

**Creation mode** — full vocabulary table grouped by cluster.

**Sync mode** — diff summary first, then full table:

````markdown
## Tag Vocabulary Preview

**Mode:** Sync | **Count:** 75 | **Fetched:** 2026-04-06

### New tags to add (3)

| Tag | Count | Cluster | Characterization |
|-----|-------|---------|-----------------|
| `basis-ecosystem` | 12 | Architecture | Foundational stack choices and tradeoffs |

### Tags to remove (1)

| Tag | Count | Cluster | Characterization |
|-----|-------|---------|-----------------|
| `old-unused-tag` | 2 | Other | Below threshold |

### Count updates (silent, no approval needed)

- `javascript`: 398 -> 412
- `nodejs`: 280 -> 287

### Summary after changes

75 tags: 31 Ecosystem | 24 Architecture | 12 Content Type | 8 Other

Approve all, or specify adjustments (e.g. "keep old-unused-tag",
"move basis-ecosystem to Content Type", "change characterization for X").
````

Wait for user response before writing.

### Step 8: Write

After approval, build the complete file and write atomically:

```
Write(file_path="~/.claude/references/raindrop-tags.md", content="...")
```

Full-file overwrite (not incremental edit). Create `~/.claude/references/`
directory if it does not exist.

**Reincorporate preserved config fields** into the written file's YAML
frontmatter. Do not overwrite or strip `blocklist`, `context_tags`, or
`conventions` — these are user-authored and must survive the sync cycle.

### Step 9: Report

````markdown
## Tag Vocabulary Synced

File: ~/.claude/references/raindrop-tags.md
Tags: 75 (31 Ecosystem | 24 Architecture | 12 Content Type | 8 Other)
Mandatory: ai-bookmarked (always applied)

Changes: added 3, removed 1, updated 18 counts

Next: `/session-bookmarks` will use this vocabulary when creating bookmarks.
````

## Output File Format

The vocabulary file uses YAML frontmatter for machine-readable metadata
and markdown tables for human-readable content:

```markdown
---
tag_count: 75
fetched_at: 2026-04-06
mandatory_tags:
  - ai-bookmarked
blocklist:
  - "5"
  - "4"
  - "3"
  - "2"
  - imported
  - toread
  - unread
  - "for:*"
context_tags: []
conventions: []
---

# Raindrop Tag Vocabulary

Always include `ai-bookmarked`. Choose 2-5 additional tags per bookmark.

## Ecosystem

| Tag | Count | Characterization |
|-----|-------|-----------------|
| `javascript` | 412 | Core JS language, runtime, ecosystem |
| `nodejs` | 287 | Node.js server-side runtime and tooling |

## Architecture

| Tag | Count | Characterization |
|-----|-------|-----------------|
| `protocol-design` | 156 | Specs, RFCs, protocol architecture |

## Content Type

| Tag | Count | Characterization |
|-----|-------|-----------------|
| `foundational-text` | 89 | Seminal papers, essays, reference works |

## Other

| Tag | Count | Characterization |
|-----|-------|-----------------|
| `indieweb` | 67 | IndieWeb movement, ownership, POSSE |
```

