Skip to main content
Restructuring reorganizes a knowledge base from a manifest: a short YAML or JSON list of moves, merges, and edits. KiwiFS turns it into a plan you can review, then applies the whole thing as one git commit that you can revert. Every link to a moved page is respelled in the style it was written in — full path, short name, relative path, markdown link, table cell — and re-resolved to prove it still reaches the same page. A plan that would break a working link is rejected. Comments, personal state, frontmatter field ownership, and task claims follow their pages. Use it whenever you would otherwise rename files by hand and fix links afterwards: renaming a folder, archiving old drafts, folding duplicate pages together, or letting an agent reorganize a messy knowledge base under review.
Single renames go through the same engine. Moving a page from the sidebar file tree, kiwi_rename, and the REST rename endpoints all relink and never overwrite an existing destination. The Move / Rename item in a page’s More actions menu is the exception: it copies and deletes without rewriting links.

How it works

1

Write a manifest

Describe what should change. Paths are relative to the knowledge root. A destination ending in / is a folder that keeps each file’s name.
reorg.yaml
2

Plan it

Planning writes nothing. It returns every move and merge, every rewritten link, content edits, comment and state files that follow their pages, folders that become empty, warnings, errors, and a digest that identifies exactly this plan.
Terminal
A plan with errors (a taken destination, a link that cannot be kept, a protected path) cannot be applied.
3

Review it

Read the plan in the terminal (--diff prints a unified diff per page), in the web UI’s Restructure view, or as JSON. Change the manifest and plan again until it is right.
4

Apply it

Apply with the digest you reviewed. If any affected file changed since, nothing is written and you get the fresh plan to review instead. Everything lands in one commit, which the response names.
Terminal
5

Revert if needed

Revert puts every page and comment or state file back byte for byte, as a new commit. It refuses if any of those files changed after the commit, so it never discards later work. Revert requires git versioning.
Terminal

Run it

kiwifs refactor has three subcommands. apply and revert show the plan and ask for confirmation unless you pass --yes.
Terminal
If a KiwiFS server is already serving --root, apply and revert send the change to that server (at --server, default http://127.0.0.1:<port from config>) so its index and live readers stay consistent. Otherwise they apply directly. kiwifs refactor plan exits 1 when the plan has errors.See kiwifs refactor for every flag.

Manifest reference

Unknown keys are an error, so a typo cannot silently drop part of a restructuring. Selection always sees the workspace as it was before the manifest.
Destinations must not exist. Destinations that collide by case or Unicode normalization, or are not portable file names, are reported as conflicts with a code: exists, many_to_one, case_or_normalization, forbidden_chars, too_long, trailing_dot_or_space, empty_name, dir_cycle, parent_is_file, is_folder.A page claimed by another actor cannot be moved.
from is deleted. Every link, comment, and personal-state entry that pointed at it now points at into. The body of into is not changed — write the combined page first, then run the merge. into can be a current path or the path a move in the same manifest gives it.
select takes paths, glob, and a DQL query. Every criterion narrows the selection, and at least one is required (use glob: "**" to mean everything). replace only touches body text outside frontmatter and code; add regex: true to use an RE2 pattern with $1 captures.Frontmatter is edited line by line, so comments, key order, and alignment survive, and a retag is a one-line diff per page. Keys owned by another agent are refused unless the edit sets force: true.Edits can also compute values per page:
  • when: keeps only pages where an expression is true, for example year(date) < "2024" and "draft" in tags.
  • compute: {key: expr} sets a key to an expression’s value.
  • move_to: "archive/{{year}}/{{slug}}.md" moves each page to a templated path. on_conflict: suffix("-%d") numbers collisions deterministically; otherwise they are conflicts.
Expressions use expr over the page before the manifest: path, dir, name, stem, title, tags, fm, links, date, modified, now, with slug(), year(), month(), day(), and date(t, layout). A plan that uses expressions reports the now it used. Put it in the manifest (now:) or pass it to kiwi_refactor_apply so the digest still matches.
Covers frontmatter tags lists and inline #tags in the body. Renaming into a tag that already exists is a warning.
Each section becomes its own page, titled with its heading. The original page becomes an index linking to them. Links to handbook#heading follow their section, and each section’s own links are re-resolved from its new folder.
  • delete_empty_dirs: true removes folders the plan leaves empty.
  • now: pins the clock expressions see (an RFC 3339 timestamp).

Scripts

When globs, DQL, and expressions are not enough, compute the manifest with a Starlark (Python-like) program. It runs in a separate, sandboxed process and can only read: json and print are also available. Steps, memory, time, and reads are bounded, and there are no while loops or recursion. Return the manifest from main():
archive_done.star
Nothing is written. The script’s manifest is planned, and you apply it like any other with its digest. Run scripts with kiwi_refactor_script or POST /api/kiwi/refactor/script (body {"script": "..."}); the response holds manifest, output (what the script printed), and plan.

Guardrails

.kiwi/config.toml
An agent (MCP, or REST with X-Kiwi-Client: mcp/...) applying a plan that writes or deletes more than approval_files files, or rewrites more than approval_links links, needs a person’s approval:
  • If the MCP client supports elicitation, the user is asked to confirm in the client.
  • Otherwise the plan waits in GET /api/kiwi/refactor/pending until a person approves it — in the web UI’s Restructure view, or with POST /api/kiwi/refactor/approve. An approval lets the agent apply that exact digest once within the hour.
The REST apply endpoint answers 428 when approval is required.
Every plan reports integrity: how many links in the written pages point nowhere (these were already broken — the plan never breaks a working link) and which pages a merge leaves with no inbound links.stats counts pages and links checked, links rewritten, and links that already pointed nowhere and were left exactly as written.
Applying re-checks every file’s ETag inside the write lock and writes a journal of pre-images first (.kiwi/state/refactor-journal.json). If the process dies mid-apply, the next start rolls it back unless the commit landed. A failed commit rolls the whole restructuring back.The commit carries trailers: Kiwi-Actor, Kiwi-Client, Kiwi-Refactor-Digest, Kiwi-Manifest, and Kiwi-Base.

Editor integration

kiwifs lsp is a language server for markdown. When your editor is about to rename or move files (workspace/willRenameFiles), it answers with the link edits computed by the same planner. Point your editor’s generic LSP client (VS Code, Zed, Neovim) at it:
Terminal
The editor performs the rename itself, so comments and personal state do not follow the page. Use kiwifs refactor or the web UI when they matter. POST /api/kiwi/refactor/plan?format=workspace-edit returns a plan as an LSP WorkspaceEdit, and POST /api/kiwi/refactor/from-workspace-edit turns an editor’s renames into a manifest and plan (its text edits are recomputed, not trusted).

REST endpoints

All paths are under /api/kiwi. Pass X-Actor to record who made the change.

Wiki links

How links resolve, which is what restructuring preserves.

MCP

Connect agents and use the kiwi_refactor_* tools.

Versioning

Git history behind every apply and revert.

CLI reference

Flags for kiwifs refactor and kiwifs lsp.
Last modified on October 19, 2026