---
name: link-desk
description: >-
  Find internal links a content site is missing, matched by what the reader needs next rather than shared keywords, and hand an editor a review page with the exact place, anchor and reason for each link. Use when the user says missing links, internal linking, link opportunities, orphan articles, interlink the site, or wants to connect articles across sister sites.
allowed-tools:
  - Bash
  - Read
  - Write
  - Glob
  - WebFetch
department: seo
---

# /link-desk — Missing Internal Links, Reviewed by a Human

**Local overlay.** If `~/.local/share/flywheel/house/link-desk.md` exists, read it before starting and apply it on top of this skill: it holds an organisation's own rules, tools and paths for this workflow. Where the two conflict, the overlay wins. No overlay, no change.

Reads every article on a site, decides where each reader should go next, proves the link is missing, and produces a page where an editor accepts or skips each suggestion. Nothing on the site is changed by this skill.

`screamingfrog-check` finds broken and orphaned pages. `content-gate` checks a piece before it ships. This skill sits between them: it finds links that are technically fine to add and editorially worth adding.

## User-invocable
When the user types `/link-desk <site>`, run this skill.

## Arguments
- `/link-desk https://looksmaxxing.guide/en/` — whole site, default top 15
- `/link-desk https://looksmaxxing.guide/en/ --sections looks,fitness,dating --top 30`
- `/link-desk https://looksmaxxing.guide/en/ --cross gangnambeautyguide.com,hybridathlete.guide` — also suggest links to sister sites

## Instructions

### Step 1: Inventory
Read `sitemap.xml` (fall back to crawling nav links). Keep article URLs that return 200 and are indexable. Exclude utility pages (about, legal, tag archives) and, unless asked, programmatic pages (for example `/influencers/`). These remaining pages are the **targets**. The articles in `--sections` are the **sources**.

### Step 2: Read each source by structure
For every source, keep only the article body (on Astro sites the `#article-body` element up to the closing `</article>`; never count nav, footer, "Popular searches" or table-of-contents links). Build a card:
- title, section, H2 list with their `id`s
- the paragraphs under each H2
- links already present in the body

Save cards as JSON. Do not paste whole pages into context; the cards are what you reason over.

### Step 3: Match by the reader's next step
For each source, read its sections and ask: where does this reader need to go next, and which existing page answers that? Typical signals:
- the text already promises another article ("We have a separate article on …") but does not link it
- a section ends at a decision the reader now faces (finished the beginner program → which split)
- a section gives the "why" and another page holds the "how" (cardio says fat loss comes from eating less → weight loss playbook)
- a risk article should point to the safer alternative

Shared keywords are not a reason. Pick at most 1–2 links per source, and leave a source alone when nothing fits.

### Step 4: Prefer words already on the page
If the section already contains words that describe the target, link those words. Only when no such words exist, write one short sentence that matches the article's voice. Record which case each suggestion is.

### Step 5: Prove every suggestion before showing it
Reject the suggestion unless all of these hold:
1. The target returns 200 today.
2. The target is not already linked inside the source body.
3. The anchor text appears exactly once in that section.
4. The source section `id` exists, so the editor can jump straight to it.
5. Pages about medical risk, drugs or self-harm only point to safer options, never to procedures or products. Judge the whole target body, not just its topic: if a safe target ends with a clinic, procedure or product block, keep the suggestion only with a visible warning on its card.

Re-fetch the live page for checks 1–2; cached crawls go stale.

### Step 6: Review page and export
Write a single static HTML page:
- the clearest suggestion first
- each suggestion shows the source paragraph with the new link highlighted (or the new sentence marked), the section, the target, and one line of "why"
- Accept / Skip per row, filters by section, and **Export JSON**:
  `{ source, section_id, target, change: { type: link_existing_text | add_sentence, anchor | sentence }, decision }`

Publish only if the user asks; the page holds public data only.

### Step 7: Cross-property mode (`--cross`)
Run the same steps with sister-site pages as targets. Keep the group's conventions on every cross-site link: `rel="sponsored noopener"` and `utm_campaign=cross-property` with `utm_content=<source-slug>`. Flag existing sister blocks that point to a homepage when a matching deep page exists.

## Output
- `cards.json`, `suggestions.json`, `index.html` in the working folder
- A short summary: sources read, suggestions kept, how many reuse existing words vs need a new sentence, and anything rejected in Step 5 with the reason

## Alternatives (when this is the wrong tool)
- **One-off audit for a single article:** skip the page, return the 1–3 suggestions inline.
- **Very large networks (hundreds of sites):** use local embeddings to shortlist the top 5 targets per source, then apply Steps 3–5 only to the shortlist.
- **Automatic insertion:** not recommended. Wording and medical context need a human; export JSON and let the CMS apply accepted rows.
