Scan the vault (or a target directory) for tags used in page frontmatter and page bodies, diff against the vault's 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.
Scanned 8/31/2026
Install to Claude Code
npx -y skills add ihudak/ihudak-claude-plugins --skill wiki-tags-refresh --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Wiki Tags Refresh?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/ihudak-wiki-tags-refresh)More formats (shields.io, HTML) on the badges page.
---
name: wiki-tags-refresh
description: >
Scan the vault (or a target directory) for tags used in page frontmatter and page
bodies, diff against the vault's 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.
allowed-tools: Read Write Edit Glob Grep Bash
---
# wiki-tags-refresh
Read `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 and scan scope
Vault path: `~/obsidian_vault` by default; set `VAULT_PATH` to override. WSL users with a Windows-side vault must set `VAULT_PATH` explicitly (e.g. `/mnt/c/Users/<name>/obsidian_vault`).
Scan scope: the vault root (default) or the given directory argument.
---
## 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 scanned pages
Scan every `.md` file under the scan scope for frontmatter `tags:` entries only.
A broad grep would also match `related:` wikilinks — use the awk approach below
to stay strictly within the `tags:` block:
```bash
find "<scope>" -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: tags must use the YAML multi-line block form (`tags:` followed by ` - tagname` lines); inline arrays (`tags: [a, b]`) are silently skipped by this script. `-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/_-]*" "<scope>" | sort -u
```
Exclude false positives: markdown headings at line start (`^#`), code blocks, URLs.
Deduplicate the full collected set.
---
## 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 found): #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
Always confirm zero vault-wide use before suggesting removal — the Step 3 scan may have
been narrower than the full vault. Exclude only `tag-index.md` itself (not all of
`.obsidian/`): every documented tag appears in `tag-index.md` by definition, so excluding
the whole `.obsidian/` directory would also hide genuine uses in other `.obsidian/` files
(e.g. Copilot custom prompts), silently dropping their removal protection.
```bash
grep -roh --include="*.md" --exclude="tag-index.md" "#[a-zA-Z][a-zA-Z0-9/_-]*" "<VAULT_PATH>" \
| grep -xF "#tagname" | wc -l
```
The exact-match filter (`-xF`) avoids `#tagname` false-matching a longer tag like
`#tagname-extended` or `#tagname/nested`.
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.
```
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!