Wiki Tend
One-shot maintenance: status, audit, normalize (tags + cross-links). Report first, repair only what's safe, and degrade gracefully wherever a convention the step depends on is absent.
<!-- ws:core -->
The wiki model
A wiki is a folder whose
carries a
heading.
is the navigation contract: every space directly inside is listed there as
- [label](path/index.md) — description
, and tools traverse only what it lists. Spaces nest recursively; each space is itself a wiki one level down. Plain folders (no
) just group files. The markdown dialect is Obsidian — wikilinks, frontmatter, callouts, embeds; the companion
and
skills cover the syntax.
Resolving the wiki
Resolution order: an explicit path from the user → the nearest CWD-ancestor folder that is a wiki, or carries a
folder that is one (a folder's own space) → the
key in
~/.config/wiki-spaces/config
. The user's words override the mechanics: "my wiki" means the configured one even when CWD sits inside another wiki (a company repo, say). When a CWD wiki and a different configured wiki both exist, announce which root you resolved; ask once if intent is ambiguous. Never silently operate on the wrong wiki.
The bundled script
sits next to this SKILL.md — stdlib python3 (3.9+), zero dependencies, read-only. It parses the contract, never the content: structure — traversal, scope, caps, drift — is the script's side; reading and judging meaning is yours. Invoke it by absolute path (your working directory is usually elsewhere):
python3 <skill-dir>/scripts/ws.py list --wiki <root>
— spaces reachable via the contract, each with its entry description ( to cross mounts).
- — markdown files reachable via the contract.
… grep <pattern> [-i] [-F] --wiki <root>
— regex line search over those files, for a literal string (a name carrying metacharacters sweeps exact); prints , exits 1 on no match. The sweep primitive: a link worklist, a tag inventory, an escaping-reference check are each a pattern plus your judgment on the hits.
… check-size <target> [--stdin] --wiki <root>
— cap verdict for a file; pipe planned content with to check before writing.
- — contract drift, entries crossing a space boundary, over-cap or unreadable files, unhealthy mounts. Findings name their repair where one is safe to name (a prints the exact line to add); apply repairs as ordinary edits and re-run the audit to verify — the script never writes.
Trust the script's output over re-deriving structure by hand. Stdout is data; stderr carries the resolved root (
prints it as its stdout header instead) and
advisories naming whatever a walk skipped, the enclosing wiki when the root is nested inside one, and the configured wiki when the root is another — relay them when they could change the answer.
Trust scope and size discipline
Owned vs external is relative to the resolved root: anything under a folder named
(at any depth), a git submodule, or a symlink into either is external; a symlink to a folder outside the tree answers to where it sits, like a clone. Reads cross owned spaces by default and enter external ones only when the user explicitly asks. Writes stay inside the targeted space; any other space — owned or external — is written only on explicit instruction.
Caps are UTF-8 bytes including frontmatter, keyed by basename:
5000,
and
100000, any other
15000.
- Caps belong to the user: a wiki that wants different ones sets them in , and reads them.
- Check caps with : pipe the planned content via before every write, new page or edit — a write that shrinks an over-cap file reports , progress, so repairs converge; a file checked on disk answers to the cap alone.
- An overflow is a signal about shape, not just size: distill the page, reshape the space, or promote a page that has grown into one — never truncate, never raise the cap.
- The exception is : an over-cap log rolls — move it whole to
_archives/log-<YYYYMMDD>.md
and start a fresh one — so the history archives, never shrinks.
Conventions are opt-in per wiki. Read the markers present at the scope root (the space the user targeted, else the resolved root) —
,
,
,
, frontmatter on pages,
,
,
— and degrade gracefully when one is absent. If
exists, append one line per operation:
sh
printf '%s %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '<OP> <details>' >> <scope-root>/log.md
<!-- /ws:core -->
Procedure
-
Resolve the wiki (core block) and announce the root when it came from CWD or could be ambiguous. A freshly scaffolded wiki has nothing to tend — say so plainly rather than inventing work.
-
Detect conventions at the scope root (core block). Presence markers (
, taxonomy,
) never inherit from a parent;
,
, and
follow their nearest-ancestor rule. Skip every mode whose marker is absent.
-
Pick the mode from the user's words:
| User says | Mode |
|---|
| "wiki status", "what's in my wiki" | status |
| "audit", "health check", "what needs fixing" | audit |
| "fix tags", "normalize tags" | normalize: tags |
| "link pages", "cross-reference" | normalize: links |
| "tend wiki", "clean wiki" | full sweep: status → audit → ask → normalize |
Status and audit only report. Normalize previews its changes and asks before writing — unless the user already said "fix" or "apply".
-
Status. +
for the shape (add
only when the user opted in). Report: spaces and their one-line descriptions, page counts per top-level folder, tag counts and the top 10 by usage (from a
sweep, when frontmatter is in use), the last
line (when present), and git status (when
exists — read-only; never commit or push).
-
Audit. Run
and relay its findings in its own vocabulary. The script is the source of truth for these; don't re-derive them:
- — a bare index (registered or not), a near-miss heading, a malformed or duplicate entry, an unregistrable child name, an entry naming a file rather than a space, an entry crossing another space's boundary, a dead second heading.
- — an on-disk space not listed, or a listed entry with nothing on disk.
- — a registered external mount that is no longer a wiki.
- and files.
Add the judgment the script can't make:
- Remediation suggestions. For each over-cap file, suggest what its diagnosis calls for — distill (bloat) or reshape the space (siblings under a hub, a promotion, a regrouped layout), per 's overflow procedure; an over-cap takes the archive roll instead (core block).
- Link integrity — content, so yours: sweep the link shapes (, ), judge each hit against the inventory and the disk, and report dead ones with the nearest-named page as the likely target — a body is exempt, its placeholder links being examples, not dead links. On a large wiki, sweep the spaces this pass touches and say what you skipped.
- Frontmatter completeness — only on pages that already carry frontmatter, only when the wiki uses a schema; mixed adoption is allowed.
- Structural prompts — a page pressing its cap ( any page you suspect) with several distinct H2 sections is outgrowing its shape and is reshape-ready; a space whose index lists many accreted siblings wants a sub-space; a long-grown opt-in wants distilling into cold pages; a space holding one small page and no sub-spaces reads as over-structure — suggest demoting it to a page ('s restructure reference).
-
Structural repair (close-out). Apply the safe structural repairs yourself, one edit each, re-auditing between rounds until the structural findings are gone — each re-audit re-derives findings from disk, so the rounds converge:
<!-- ws:safe-repairs -->
- Add the exact entry line a finding prints, then trail it with a — the placement hint every later operation reads.
- Insert the heading a registered bare child lacks — only where the finding's hint asks for the insert; a near-miss hint asks for a rename, which stays reported as author intent.
- Remove an entry the audit says crosses another space's boundary — that space owns the deeper listing; register it there instead when it is missing.
<!-- /ws:safe-repairs -->
Everything else is author intent — malformed or duplicate entries, unregistrable child names, entries naming files, near-miss headings, dead second headings, undeclared bare indexes, stale entries, unhealthy mounts, over-cap or unreadable files, and any dead links your own sweep surfaced — and stays reported for the user (offer to fix as a normal edit if the user says go; an undeclared bare index is a promotion decision).
-
Normalize: tags. Only when
exists at the scope root, and only over the scope's own pages — children spaces keep their own taxonomies; leave them out.
- Sweep the inventory with
grep '^tags:' --wiki <root>
; read the frontmatter of any page the sweep leaves ambiguous — YAML shapes are yours to judge.
- Untagged pages are minus the sweep's pages, less the frontmatter-exempt special files: indexes, , , .
- Flag non-canonical tags, over-tagged (>5) pages, and untagged pages; apply the alias mappings the taxonomy defines.
- An unknown tag on 2+ pages: suggest adding it to the taxonomy. A one-off: suggest the closest canonical tag.
-
Normalize: cross-links. Build a registry of scope-own pages (names, titles, frontmatter aliases when present); the special files (indexes,
,
,
) stay out of both the registry and the scan — navigation, history, scratch, and boilerplate neither give nor take links. Scan bodies for unlinked first mentions of registry entries and wrap the strongest matches as wikilinks — at most 2 new links per page, never inside code blocks or frontmatter, no self-links. Prefer inline linking; a trailing
section is the fallback when no natural mention exists. On a large wiki, scan the spaces this pass touches and say what you skipped.
-
Log one
line per the core block when
exists, and close with the report.
Output
Per mode: a status table; an audit report quoting the script's findings plus your suggestions; normalize tables (tag → action, link added → where). End the full sweep with the one-line delta:
issues before → after; left for you: <n>
.