Use when an Obsidian vault's flat tags should be organized into a nested hierarchy over EXISTING tags, OR when a bilingual vault has the same concept tagged in two languages that should be merged. Trigger phrases - "organize tags", "tag hierarchy", "group my tags", "tag structure", "nest my flat tags", "tag optimization", "restructure tags", "structure my tags", "merge my German and English tags", "merge cross-language tags", "bilingual tag merge", "DE/EN tag merge", "merge duplicate tags in ...
Scanned 9/2/2026
Install to Claude Code
npx -y skills add neckarshore-skills/obsidian-vault-autopilot --skill tag-organize --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Tag Organize?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/neckarshore-skills-tag-organize)More formats (shields.io, HTML) on the badges page.
---
name: tag-organize
description: Use when an Obsidian vault's flat tags should be organized into a nested hierarchy over EXISTING tags, OR when a bilingual vault has the same concept tagged in two languages that should be merged. Trigger phrases - "organize tags", "tag hierarchy", "group my tags", "tag structure", "nest my flat tags", "tag optimization", "restructure tags", "structure my tags", "merge my German and English tags", "merge cross-language tags", "bilingual tag merge", "DE/EN tag merge", "merge duplicate tags in two languages". Runs AFTER tag-manage cleanup (hygiene first). Proposes structure and cross-language merges over existing tags only; it does NOT invent tags from note content (that is the later auto-tag slice, not yet built).
---
# Tag Organize
Propose a nested tag hierarchy for a vault's flat, sprawling tags. The model
groups existing flat tags into candidate parent/child families; you review,
disambiguate, and approve; the deterministic engine applies the nests.
This is the AI-driven companion to `tag-manage`. `tag-manage` does hygiene
(rename/merge/remove, and *declared* nesting from config). `tag-organize`
*proposes* the structure. The two share one engine (`skills/tag-manage/scripts/`);
this skill adds the proposal + orchestration layer.
## What this does / does not do (Slice 1)
- **Does:** scan the existing flat tags, propose parent/child families by shared
name (e.g. `Business-Strategy` + `BusinessModel` + `business-dev` -> parent
`Business`), and turn the families you approve into nests via the existing
declared-hierarchy path.
- **Reads content only to disambiguate:** for a family the names alone do not
settle (homonyms, mixed-sense stems), read a small bounded sample of note
bodies — behind the content-read gate below.
- **Does NOT auto-assign tags to notes.** Filling under-tagged notes with new
tags from their content is the later auto-tag slice (`addTagsToNote`), not yet
built. This skill only reshapes tags that already exist.
## Principle: Core + Nahbereich + Report
- **Core:** propose families, apply the approved nests.
- **Nahbereich:** none that writes. Surface adjacent observations (e.g. a tag
that looks mis-cased, a near-duplicate) for `tag-manage`; do not fix them here.
- **Report:** every run ends with what was proposed, approved, applied, and
deferred. The report is how the user knows what happened.
## Prerequisite: run hygiene first
Recommend running `tag-manage` (audit + cleanup) **before** organizing, so the
structure is induced over a clean tag set rather than a messy one (mixed casing,
duplicates, separator variants). If the user has not cleaned up recently, say so
and offer to run `tag-manage` first.
## Production Vault Safety Rules
Production-vault runs follow the repo's Production Vault Safety Rules:
- **Test vault first.** Validate on a throwaway copy before any production run.
- **Gate, not step.** Switching from a test vault to a production vault requires
an explicit user confirmation, even for read-only scans.
- **No filesystem discovery.** Operate only on the vault path the user provides;
never scan outside `${OBSIDIAN_VAULT_PATH}`.
- **Confirm before bulk.** Before applying nests that touch more than 10 notes,
state "I will nest tags in N notes in `<vault>`. Confirm?" and wait.
## Flow
Set `OBSIDIAN_VAULT_PATH` (or pass the vault path explicitly). The engine is the
shared `tag-manage` CLI.
**1. Induce candidate families (read-only):**
```bash
node "${CLAUDE_PLUGIN_ROOT}/skills/tag-manage/scripts/cli.js" induce "${OBSIDIAN_VAULT_PATH}"
```
This writes `.tag-organize-clusters.json` (a dot-prefixed sidecar, never scanned) and,
when a report home is configured, a browsable `<date> Tag Organize Proposal - Vault-wide.md`
note — the human-readable view of the same families. Read either.
**2. Present the proposal (three tables).** `induce` scores each family by structural
signal (family size, note frequency, enumeration suffix, match against a declared config
parent, minus a coincidence-prefix penalty) and splits them into three sections in the
proposal note:
- **Implement** — high structural confidence (e.g. enumerated families like `Phase0-4`, or
a parent that matches a declared config parent). Propose the whole bucket as a default
batch: show the one table, let the user deselect any they reject, then persist + apply
the rest.
- **Decide** — the uncertain middle. Work through individually; for families whose names do
not settle the call, use the content-read gate below.
- **Ignore** — likely name-coincidence (a common-word prefix, e.g. `Open` over `OpenAI` +
`OpenSource`). Skipped by default; the user may still promote one.
Each row carries `Notes` (how many notes the family touches) and `Score` (0–100). The score
is a structural signal strength, **not** a probability — use it with `Notes` to triage.
`Implement` is a recommendation, never auto-apply: every nest still goes through the confirm
gate in step 5.
**3. Disambiguate the uncertain ones (content-read gate).** For families whose
placement the names do not settle, read a bounded sample of note bodies to decide.
State the scope first: "I will read up to N notes across M families to confirm
their grouping — proceed?" Wait for approval. Sample only the notes carrying the
ambiguous tags (top few per tag), never the whole vault.
**4. Persist each approved cluster** (deterministic, no LLM):
```bash
node "${CLAUDE_PLUGIN_ROOT}/skills/tag-manage/scripts/cli.js" set-hierarchy "${OBSIDIAN_VAULT_PATH}" \
--parent Business --children Business-Strategy,BusinessModel,business-dev
```
`set-hierarchy` refuses to persist an invalid taxonomy (it throws). Re-run it once
per approved cluster. The user may rename a proposed parent before you persist it
(the proposal's casing is a starting point, not a mandate).
**5. Apply the nests via the existing guarded path.** Re-audit to surface the
nest recommendations the declared hierarchy now implies, then plan and apply:
```bash
node "${CLAUDE_PLUGIN_ROOT}/skills/tag-manage/scripts/cli.js" audit "${OBSIDIAN_VAULT_PATH}" --report-dir <dir>
node "${CLAUDE_PLUGIN_ROOT}/skills/tag-manage/scripts/cli.js" plan "${OBSIDIAN_VAULT_PATH}" --from-recs <dir>/.tag-manage-nest.json --ids 1,2
# Human gate: "I will nest tags in N notes in <vault>. Confirm?" — wait for yes.
node "${CLAUDE_PLUGIN_ROOT}/skills/tag-manage/scripts/cli.js" apply "${OBSIDIAN_VAULT_PATH}" --from-recs <dir>/.tag-manage-nest.json --ids 1,2 --write
```
A nest is a rename onto a slash path, so it rides the same `applyOps` + survival +
mass-change guards + confirm gate as every other op, and it **converges** (an
already-nested tag yields no further nest).
## Cross-language merge (Slice 2)
A bilingual vault tags the same concept twice — `Versicherung` and `Insurance`,
`Skalierung` and `Scaling` — and each half is a low-frequency singleton. This flow
finds those DE<->EN pairs and merges them. Translation is **model judgement**; the
engine does only the byte-level rename and the safety guard. There is no seeded
dictionary in v1 — you translate natively.
**1. Read the low-frequency list.** Run `audit` with a report home so the report
lists singletons + doubletons:
```bash
node "${CLAUDE_PLUGIN_ROOT}/skills/tag-manage/scripts/cli.js" audit "${OBSIDIAN_VAULT_PATH}" --report-dir <dir>
```
**2. Identify DE<->EN pairs where BOTH halves exist in the inventory.** Only propose a
merge when the target tag already exists — never invent a target tag the vault does
not use. The German half merges into the English half by **default direction by
language** (English-canonical). A German-primary vault overrides this with
`crossLanguageCanonical: "de"` in `Tag Manage Config.md` — direction is by language,
not by frequency.
**3. Split confident from borderline.** A merge with a clear translation and the same
scope is **confident**. A merge that **narrows or shifts meaning** (e.g. a broad
German term whose English candidate is more specific) is **borderline** — flag it,
read a bounded note sample under the content-read gate, and never auto-apply it.
**4. Write the confirmed merges to a sidecar** `.tag-organize-merges.json` as recs.
Each rec carries `kind: "merge"` and `source: "cross-language"` (metadata for the
report). You do **not** need a flag to arm the safety guard — the apply boundary applies
the both-exist check to any model-authored merge by default (see step 6). A DE<->EN merge
is mechanically a rename op:
```json
[
{ "kind": "merge", "source": "cross-language",
"ops": [{ "type": "rename", "from": "versicherung", "to": "Insurance" }] }
]
```
**5. Cross-language clusters use the nest path, not a flat merge.** A family like
`Fördermittel*` + `Funding` becomes a nest-under-a-parent proposal via `set-hierarchy`
(the Slice 1 flow above), not a single merge.
**6. Apply via the guarded `--from-recs` path.** Same confirm gate and guards as every
other op:
```bash
node "${CLAUDE_PLUGIN_ROOT}/skills/tag-manage/scripts/cli.js" plan "${OBSIDIAN_VAULT_PATH}" --from-recs <dir>/.tag-organize-merges.json
# Human gate: "I will merge N cross-language tag pairs in <vault>. Confirm?" — wait for yes.
node "${CLAUDE_PLUGIN_ROOT}/skills/tag-manage/scripts/cli.js" apply "${OBSIDIAN_VAULT_PATH}" --from-recs <dir>/.tag-organize-merges.json --write
```
The apply boundary runs a two-tier validator on every `--from-recs` sidecar. Tier 1
(universal) rejects any op whose `from` tag is not in the live inventory or whose
rename target is malformed. Tier 2 (the both-exist guard) is the **default** for every
rename: the merge **target** must already exist in the inventory — this is what stops a
wholesale translation of the vault's tag language. Only engine-authored recs (the nest
and convention-fold paths, whose target is legitimately new) opt out, via an
engine-set `targetMayBeNew` marker that a model sidecar never carries. So a merge you
author is held to both-exist whether or not you stamp `source` — safety is the default,
not an opt-in. Any violation aborts the run (`ABORTED`, exit non-zero, nothing written).
## Report format
End every run with:
```
## Tag Organize Report - <date>
### Done
- Proposed N candidate families
- Approved + persisted M clusters (set-hierarchy)
- Applied K nests (<note counts>)
- Merged J cross-language pairs (DE<->EN, <note counts>)
### Findings (reported, not fixed)
- <adjacent tag-hygiene observations -> tag-manage>
### Deferred
- <families left for content review, or skipped>
- <borderline cross-language pairs (meaning narrows/shifts) left unmerged>
```
## Known limitations (Slice 1)
- The parent name is the most-frequent leading display segment among members;
confirm acronym/casing and homonym placement at review (you have the content),
do not assume the proposal is final.
- No content-based auto-tagging. Under-tagged notes are not filled — that is the
later auto-tag slice (`addTagsToNote`), a separate build with its own gate.
- A configured `reportDir` (via `Tag Manage Config.md`) makes `audit` and `induce`
WRITE their report artifacts even without `--report-dir`. A "read-only audit" is not
strictly read-only when a report home is set; the dated `HHMM` filename keeps same-day
re-runs from overwriting each other.
- `_`-prefixed folders are skipped by the scan (shared with `tag-manage`). This is **not
silent**: the proposal note's **Scan Coverage** section names every skipped `_`-folder
that held markdown, so the cluster proposals are never read as covering the whole vault
when real content lives in `_Work/`, `_Personal/`, etc.
- Cross-language merge (Slice 2) is **model-driven, no seeded dictionary** — translation
quality is yours, not the engine's. The both-exist guard enforces that the merge target
already exists in the vault (safety-by-default: it applies to any model-authored merge,
stamped or not), but it **cannot catch a wrong-but-real translation** — merging into the
wrong existing tag passes the guard. Review confident vs. borderline pairs yourself; the
guard prevents inventing a target, not choosing the wrong one.
## Quality check
- [ ] Hygiene (`tag-manage`) recommended before organizing
- [ ] Content-read gated, scope stated, sample bounded
- [ ] Every nest applied only after the confirm gate; more than 10-note runs confirmed first
- [ ] Report produced
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!