ws-tend

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Wiki Tend

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 -->
一次性维护:状态、审计、规范化(标签 + 交叉链接)。先报告,仅修复安全项;当某个步骤所依赖的约定不存在时,应安全降级。
<!-- ws:core -->

The wiki model

Wiki 模型

A wiki is a folder whose
index.md
carries a
## Spaces
heading.
## Spaces
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
index.md
) just group files. The markdown dialect is Obsidian — wikilinks, frontmatter, callouts, embeds; the companion
obsidian-markdown
and
obsidian-bases
skills cover the syntax.
wiki 是一个文件夹,其中
index.md
包含一个
## Spaces
标题。
## Spaces
是导航约定:每个直接位于其中的 space 都在这里列为
- [label](path/index.md) — description
,工具也只遍历这里列出的内容。space 可以递归嵌套;每个 space 本身也是下一层的 wiki。普通文件夹(没有
index.md
)仅用于归组文件。Markdown 方言为 Obsidian——支持 wikilinks、frontmatter、callouts 和 embeds;配套的
obsidian-markdown
与
obsidian-bases
skill 介绍了相关语法。

Resolving the wiki

解析 wiki

Resolution order: an explicit path from the user → the nearest CWD-ancestor folder that is a wiki, or carries a
.wiki-spaces/
folder that is one (a folder's own space) → the
wiki
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.
解析顺序:用户提供的显式路径 → 最近的 CWD 祖先文件夹,该文件夹要么本身是 wiki,要么包含一个作为 space 的
.wiki-spaces/
文件夹 →
~/.config/wiki-spaces/config
中的
wiki
键。用户的措辞优先于机制:即使 CWD 位于另一个 wiki(例如公司仓库)中,“我的 wiki”也指已配置的 wiki。当 CWD wiki 与另一个已配置 wiki 同时存在时,应声明解析到的根目录;如果意图不明确,只询问一次。绝不能悄悄操作错误的 wiki。

The bundled script

随附脚本

scripts/ws.py
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
    ## Spaces
    contract, each with its entry description (
    --external
    to cross mounts).
  • … files --wiki <root>
    — markdown files reachable via the contract.
  • … grep <pattern> [-i] [-F] --wiki <root>
    — regex line search over those files,
    -F
    for a literal string (a name carrying metacharacters sweeps exact); prints
    rel:line: text
    , 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
    --stdin
    to check before writing.
  • … audit --wiki <root>
    — 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
    missing entry
    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 (
audit
prints it as its stdout header instead) and
note:
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.
scripts/ws.py
位于本 SKILL.md 旁——使用标准库 python3(3.9+),零依赖,只读。它只解析约定,绝不解析内容:结构——包括遍历范围、作用域、上限和漂移——由脚本负责;读取内容并判断含义由你负责。应通过绝对路径调用它(你的工作目录通常位于其他位置):
  • python3 <skill-dir>/scripts/ws.py list --wiki <root>
    ——列出通过
    ## Spaces
    约定可访问的 space,以及每个 space 的条目描述(
    --external
    可用于跨挂载点查询)。
  • … files --wiki <root>
    ——列出通过该约定可访问的 Markdown 文件。
  • … grep <pattern> [-i] [-F] --wiki <root>
    ——对这些文件执行正则行搜索;
    -F
    用于字面字符串搜索(可精确清理名称中含元字符的内容);输出
    rel:line: text
    ,无匹配时退出码为 1。扫描原语:链接工作列表、标签清单和转义引用检查,均由模式搜索加上你对结果的判断构成。
  • … check-size <target> [--stdin] --wiki <root>
    ——判断文件是否超出上限;可通过
    --stdin
    传入计划写入的内容,在写入前进行检查。
  • … audit --wiki <root>
    ——检查约定漂移、跨越 space 边界的条目、超限或无法读取的文件,以及不健康的挂载点。发现的问题会在能够安全确定修复方式时指出修复方法(
    missing entry
    会打印需要添加的确切行);将修复作为普通编辑应用,然后重新运行 audit 进行验证——脚本本身绝不写入。
应信任脚本输出,而不是手动重新推导结构。Stdout 是数据;stderr 会携带解析到的根目录(
audit
则将其作为 stdout 标题输出),以及以
note:
开头的建议,说明遍历跳过了哪些内容、根目录嵌套在另一个 wiki 中时的外层 wiki,或根目录不是已配置 wiki 时的已配置 wiki——当这些信息可能改变结论时应进行转述。

Trust scope and size discipline

信任范围与大小约束

Owned vs external is relative to the resolved root: anything under a folder named
shared/
(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:
index.md
5000,
log.md
and
hot.md
100000, any other
*.md
15000.
  • Caps belong to the user: a wiki that wants different ones sets them in
    _meta/limits.md
    , and
    check-size
    reads them.
  • Check caps with
    check-size
    : pipe the planned content via
    --stdin
    before every write, new page or edit — a write that shrinks an over-cap file reports
    ok
    , 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
    log.md
    : 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) —
log.md
,
_meta/taxonomy.md
,
_meta/limits.md
,
_meta/ignore.md
, frontmatter on pages,
_template.md
,
hot.md
,
.git
— and degrade gracefully when one is absent. If
log.md
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 -->
归属与外部状态均相对于解析到的根目录判断:任何位于名为
shared/
的文件夹下(无论深度如何)、属于 git submodule,或是指向这两类位置的 symlink,都视为外部内容;指向树外文件夹的 symlink 则以其实际所在位置为准,就像 clone 一样。默认情况下,读取操作可跨越归属范围内的 space;只有用户明确要求时才能进入外部 space。写入操作仅限目标 space;任何其他 space——无论归属内还是外部——都只能在用户明确指示后写入。
上限按包含 frontmatter 在内的 UTF-8 字节数计算,并以文件 basename 为键:
index.md
为 5000,
log.md
和
hot.md
为 100000,其他所有
*.md
为 15000。
  • 上限归用户所有:wiki 如需采用不同上限,可在
    _meta/limits.md
    中设置,
    check-size
    会读取这些值。
  • 使用
    check-size
    检查上限:每次写入、创建新页面或编辑前,都应通过
    --stdin
    传入计划写入的内容——将超限文件缩减到上限以内的一次写入会报告
    ok, progress
    ,使修复逐步收敛;磁盘上的文件仅依据上限进行判断。
  • 溢出不仅表示文件过大,也是在提示结构形态:应提炼页面、重构 space,或将已 grown 成独立主题的页面提升为独立 space——绝不能截断,也绝不能提高上限。
  • log.md
    是例外:超限日志需要滚动归档——将整个文件移动到
    _archives/log-<YYYYMMDD>.md
    ,然后新建一个日志文件——这样历史会被完整归档,而不会缩水。
约定由每个 wiki 自行选择启用。读取作用域根目录(用户指定的 space,否则为解析到的根目录)中存在的标记——
log.md
、
_meta/taxonomy.md
、
_meta/limits.md
、
_meta/ignore.md
、页面上的 frontmatter、
_template.md
、
hot.md
、
.git
——当某项不存在时应安全降级。如果存在
log.md
,每次操作追加一行:
sh
printf '%s %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '<OP> <details>' >> <scope-root>/log.md
<!-- /ws:core -->

Procedure

操作流程

  1. 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.
  2. Detect conventions at the scope root (core block). Presence markers (
    log.md
    , taxonomy,
    hot.md
    ) never inherit from a parent;
    _meta/limits.md
    ,
    _meta/ignore.md
    , and
    _template.md
    follow their nearest-ancestor rule. Skip every mode whose marker is absent.
  3. Pick the mode from the user's words:
    User saysMode
    "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".
  4. Status.
    list
    +
    files
    for the shape (add
    --external
    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
    grep '^tags:'
    sweep, when frontmatter is in use), the last
    log.md
    line (when present), and git status (when
    .git
    exists — read-only; never commit or push).
  5. Audit. Run
    audit
    and relay its findings in its own vocabulary. The script is the source of truth for these; don't re-derive them:
    • contract
      — 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.
    • drift
      — an on-disk space not listed, or a listed entry with nothing on disk.
    • mount
      — a registered external mount that is no longer a wiki.
    • over-cap
      and
      unreadable
      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
      ws-update
      's overflow procedure; an over-cap
      log.md
      takes the archive roll instead (core block).
    • Link integrity — content, so yours: sweep the link shapes (
      grep -F '[[' …
      ,
      grep -F '](' …
      ), judge each hit against the
      files
      inventory and the disk, and report dead ones with the nearest-named page as the likely target — a
      _template.md
      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 (
      check-size
      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
      hot.md
      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 (
      ws-update
      's restructure reference).
  6. 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
      missing entry
      finding prints, then trail it with a
      — description
      — the placement hint every later operation reads.
    • Insert the
      ## Spaces
      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).
  7. Normalize: tags. Only when
    _meta/taxonomy.md
    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
      files
      minus the sweep's pages, less the frontmatter-exempt special files: indexes,
      log.md
      ,
      hot.md
      ,
      _template.md
      .
    • 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.
  8. Normalize: cross-links. Build a registry of scope-own pages (names, titles, frontmatter aliases when present); the special files (indexes,
    log.md
    ,
    hot.md
    ,
    _template.md
    ) 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
    ## Related
    section is the fallback when no natural mention exists. On a large wiki, scan the spaces this pass touches and say what you skipped.
  9. Log one
    TEND
    line per the core block when
    log.md
    exists, and close with the report.
  1. 解析 wiki(核心区块),当根目录来自 CWD 或可能存在歧义时予以声明。刚搭建好的 wiki 没有需要维护的内容——应直接说明,而不要凭空创造工作。
  2. 检测作用域根目录中的约定(核心区块)。存在性标记(
    log.md
    、taxonomy、
    hot.md
    )绝不从父级继承;
    _meta/limits.md
    、
    _meta/ignore.md
    和
    _template.md
    遵循最近的祖先规则。缺少相应标记的模式一律跳过。
  3. 根据用户的措辞选择模式:
    用户说模式
    “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 和 audit 只进行报告。normalize 会先预览变更,并在写入前询问——除非用户已经说过“fix”或“apply”。
  4. Status。 使用
    list
    和
    files
    获取结构概况(仅在用户选择加入外部内容时添加
    --external
    )。报告:各 space 及其单行描述、每个顶层文件夹的页面数量、标签数量及使用率最高的前 10 个标签(当 wiki 使用 frontmatter 时,通过
    grep '^tags:'
    扫描获取)、
    log.md
    的最后一行(如果存在),以及 git status(当存在
    .git
    时;只读,绝不 commit 或 push)。
  5. Audit。 运行
    audit
    ,并使用脚本自身的术语转述发现的问题。这些结果是脚本的事实来源;不要重新推导:
    • contract
      ——裸 index(无论是否已注册)、近似但不完全匹配的标题、格式错误或重复的条目、无法注册的子项名称、指向文件而非 space 的条目、跨越另一个 space 边界的条目,以及失效的第二个标题。
    • drift
      ——磁盘上存在但未列出的 space,或已列出但磁盘上不存在的条目。
    • mount
      ——已注册但已不再是 wiki 的外部挂载点。
    • over-cap
      和
      unreadable
      文件。
    补充脚本无法判断的内容:
    • 修复建议。 对于每个超限文件,根据其诊断结果给出建议——内容膨胀时进行提炼,或重构 space(将同级页面归入 hub、提升为独立页面或重新组织布局),具体遵循
      ws-update
      的溢出处理流程;超限的
      log.md
      则采用归档滚动方式(核心区块)。
    • 链接完整性——这属于内容判断,由你负责:扫描链接形态(
      grep -F '[[' …
      、
      grep -F '](' …
      ),根据
      files
      清单和磁盘内容判断每个命中项,并报告失效链接及其最可能对应的近似名称页面——
      _template.md
      正文中的占位链接属于示例,不算失效链接。对于大型 wiki,只扫描本次涉及的空间,并说明跳过了哪些内容。
    • Frontmatter 完整性——只检查已经包含 frontmatter 的页面,并且仅当 wiki 使用 schema 时检查;允许采用程度不一致的情况。
    • 结构性提示——某个页面接近大小上限(对任何可疑页面运行
      check-size
      ),且包含多个不同的 H2 章节,说明其结构已难以承载内容,可以进行重构;某个 space 的 index 列出了大量不断累积的同级页面,意味着应创建子 space;长期增长的 opt-in
      hot.md
      应提炼为冷页面;某个 space 仅包含一个小型页面且没有子 space,说明结构过度,应建议将其降级为普通页面(参考
      ws-update
      的 restructure 流程)。
  6. 结构修复(收尾)。 自行应用安全的结构修复,每次只做一项编辑,并在每轮修复之间重新运行 audit,直到结构问题消失——每次重新 audit 都会从磁盘重新推导发现的问题,因此各轮修复会逐步收敛:
    <!-- ws:safe-repairs -->
    • 添加
      missing entry
      发现项所打印的确切条目行,并在后面补上
      — description
      ——这是后续所有操作都会读取的放置提示。
    • 为缺少标题的已注册裸子项插入
      ## Spaces
      标题——仅在发现项的提示明确要求插入时执行;如果提示要求重命名近似标题,则保留为待作者确认的意图。
    • 删除 audit 判定为跨越另一个 space 边界的条目——更深层的列表应由该 space 管理;如果那里缺少注册项,则改为在那里注册。
    <!-- /ws:safe-repairs -->
    其他所有问题都属于作者意图——格式错误或重复的条目、无法注册的子项名称、指向文件的条目、近似标题、失效的第二个标题、未声明的裸 index、过期条目、不健康的挂载点、超限或无法读取的文件,以及你的扫描发现的任何失效链接——这些应继续报告给用户(如果用户说“go”,可将其作为普通编辑进行修复;未声明的裸 index 属于是否提升的决策)。
  7. Normalize:标签。 仅当作用域根目录中存在
    _meta/taxonomy.md
    时执行,并且只处理该作用域自己的页面;子 space 保留各自的 taxonomy,不纳入本次处理。
    • 使用
      grep '^tags:' --wiki <root>
      扫描清单;读取扫描结果无法明确判断的页面的 frontmatter——YAML 结构由你判断。
    • 未加标签的页面为
      files
      减去扫描得到的页面,再减去免用 frontmatter 的特殊文件:index、
      log.md
      、
      hot.md
      和
      _template.md
      。
    • 标记非规范标签、标签过多的页面(超过 5 个)以及未加标签的页面;应用 taxonomy 定义的别名映射。
    • 如果未知标签出现在 2 个或更多页面上,建议将其加入 taxonomy;如果只出现一次,建议使用最接近的规范标签。
  8. Normalize:交叉链接。 建立作用域自有页面的注册表,包括名称、标题和存在时的 frontmatter aliases;特殊文件(index、
    log.md
    、
    hot.md
    、
    _template.md
    )既不加入注册表,也不参与扫描——导航、历史、临时内容和模板既不提供链接,也不接收链接。扫描正文,查找注册表条目在首次出现时未加链接的情况,并将最合适的匹配项包装为 wikilinks——每页最多新增 2 个链接,绝不放在代码块或 frontmatter 中,也不得添加自链接。优先使用行内链接;只有在没有自然提及位置时,才使用末尾的
    ## Related
    章节。对于大型 wiki,只扫描本次涉及的空间,并说明跳过了哪些内容。
  9. 当存在
    log.md
    时,按照核心区块记录一行
    TEND
    日志,并以报告结束。

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>
.
根据模式输出:status 表格;引用脚本发现项并附上建议的 audit 报告;normalize 表格(标签 → 操作,新增链接 → 位置)。完整扫描最后以一行变化摘要结束:
issues before → after; left for you: <n>
。