# Wiki Tags Refresh

> Scan the vault (or a target directory) for tags used in page frontmatter and page bodies, diff against the vault's .obsidian/copilot/tag-index.md, prompt the user to approve new tags and clean stale ones, then update tag-index.md in place. Run after heavy ingest or other large content changes when new concepts may have introduced new tags. Triggers on: wiki-tags-refresh, refresh tags, update tag index, wiki tags, sync tags.

- Skill: `ihudak/wiki-tags-refresh` (Agent Skill)
- Install (CLI): `npx skillmds@latest add ihudak/wiki-tags-refresh`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ihudak/wiki-tags-refresh/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: ihudak (https://skillmd.com/u/ihudak)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ihudak/wiki-tags-refresh

---


# wiki-tags-refresh

Read `~/.copilot/installed-plugins/ihudak-copilot-plugins/obsidian-llm-wiki/skills/wiki-schema/SKILL.md` fully before proceeding.

Invoke with: `/wiki-tags-refresh [directory]`

If no directory argument is given, scan the entire vault (excludes `.obsidian/`).

---

## Step 1 — Resolve vault path

Set `VAULT` using the recommended fallback pattern:
```bash
VAULT="${VAULT_PATH:-${VAULT:-${HOME}/obsidian_vault}}"
```
`VAULT_PATH` takes precedence; `VAULT` is accepted as an alias; `~/obsidian_vault` is the default.
WSL users with a Windows-side vault must set `VAULT_PATH` explicitly (e.g. `/mnt/c/Users/<name>/obsidian_vault`).

---

## Step 2 — Read the current tag index

Read `.obsidian/copilot/tag-index.md` fully. Extract every documented tag (lines matching
`` `#tagname` ``).

---

## Step 3 — Collect tags from pages

Resolve the scan root:
```bash
SCAN_ROOT="${VAULT_PATH:-${VAULT:-${HOME}/obsidian_vault}}"
```
If `/wiki-tags-refresh` was invoked with a directory argument, append it instead: scan
every `.md` file under the vault root by default (excludes `.obsidian/`), or under
`"${SCAN_ROOT}/<directory>"` if a directory argument was given.

The frontmatter extraction (YAML multi-line) across `$SCAN_ROOT`:
```bash
find "$SCAN_ROOT" -name "*.md" -not -path "*/.obsidian/*" -print0 | xargs -0 awk '
  FNR==1{front=0; intags=0}
  /^---/{if(front==0) front=1; else front=0; next}
  !front{next}
  /^tags:/{intags=1; next}
  /^[a-zA-Z]/{intags=0; next}
  intags && /^  - /{gsub(/^  - /, ""); print}
' | sort -u
```

> Note: only the YAML multi-line block form (`tags:` followed by `  - tagname` lines) is
> parsed; inline arrays (`tags: [a, b]`) are silently skipped by this script. Worth knowing
> now that the scan covers the whole vault, where inline-array frontmatter is common outside
> plugin-authored wiki pages. `-print0`/`xargs -0` is required — vault filenames routinely
> contain spaces (e.g. `Daily/2026-07-13 Standup.md`), which would otherwise be word-split
> into separate, unopenable arguments.

Also collect inline tags from page bodies across the same scope:
```bash
grep -roh --exclude-dir=".obsidian" "#[a-zA-Z][a-zA-Z0-9/_-]*" "$SCAN_ROOT" | sort -u
```

Exclude false positives by inspecting each match's source line: the tag pattern already
requires a letter immediately after `#`, so standard `# Heading` / `## Heading` lines never
match it. The main residual false-positive source is URL fragments (e.g. `page.md#Section`)
and code blocks — use judgment when presenting candidates in Step 6.

Deduplicate the full collected set.

This walks the vault tree twice (once for frontmatter, once for inline tags); on a large
vault expect it to take longer than the previous `wiki/`-only scan.

---

## Step 4 — Diff against tag-index.md

- **New tags**: found in scanned pages (default: entire vault) but NOT documented in `tag-index.md`
- **Stale candidates**: documented in `tag-index.md` but found in ZERO scanned pages
  (flag only — never auto-remove; they may be used in other vault files)

---

## Step 5 — Report findings

```
wiki-tags-refresh results
─────────────────────────
New (undocumented in tag-index.md):   #newtag  #anothertag
Stale (0 uses in scanned pages):      #oldtag  #unusedtag
Already in sync:                      N tags
```

---

## Step 6 — Process new tags

For each new tag, ask the user:

1. "What does `#newtag` mean?"
2. "Which section of tag-index.md should it go in?"
   Present the existing section list as choices + "Other / new section…"
3. If the user skips a tag, leave it undocumented.

---

## Step 7 — Process stale candidates

Present the stale list and ask:
- `"Remove all stale tags from index"` — removes only confirmed zero-use tags
- `"Review one by one"` — step through each with keep/remove choice
- `"Keep all (ignore)"` — leave index unchanged for these

Note: Step 3's scan may have been directory-scoped (if a directory argument was given to
`/wiki-tags-refresh`), so a "stale" candidate might still be in use elsewhere in the vault.
Always confirm zero vault-wide use before suggesting removal, using an exact tag match — a
substring match like `grep "#tagname"` would false-match `#tagname-extended` or
`#tagname/nested` — and excluding only `tag-index.md` itself: every documented tag appears
in `tag-index.md` by definition, so excluding it (not the whole `.obsidian/` directory)
avoids a false "always used" result while still letting genuine uses elsewhere in
`.obsidian/` (e.g. custom prompts) protect a tag from removal:
```bash
grep -roh --include="*.md" --exclude="tag-index.md" "#[a-zA-Z][a-zA-Z0-9/_-]*" "${VAULT_PATH:-${VAULT:-${HOME}/obsidian_vault}}" \
  | grep -xF "#tagname" | wc -l
```
If vault-wide count > 0, flag as "used elsewhere in the vault — keep?" rather than suggesting removal.

---

## Step 8 — Update tag-index.md

For approved new tags:
- Insert into the chosen section in alphabetical order within the section.
- Format: `` `#tagname` - <description> ``
- Before appending to `## Recently Approved Tags`, ensure the section exists:
  ```bash
  grep -q "^## Recently Approved Tags" ".obsidian/copilot/tag-index.md" || \
    printf "\n## Recently Approved Tags\n" >> ".obsidian/copilot/tag-index.md"
  ```
- Append to `## Recently Approved Tags`:
  `` `#tagname` (YYYY-MM-DD) - <description> ``

For confirmed removals:
- Remove the entry from its section.
- Do NOT remove from `## Recently Approved Tags` (history record).

Do not touch any section not affected by the diff.

---

## Step 9 — Append to _log.md

```markdown
## [YYYY-MM-DD] tags-refresh | N new tags proposed, M approved, L removed
- New: #tag1, #tag2 (if any approved)
- Removed: #oldtag (if any removed)
```

---

## Finish

```
wiki-tags-refresh complete.
Tags added: N   Tags removed: M   Unchanged: K
tag-index.md updated.
```

