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.A plan with errors (a taken destination, a link that cannot be kept, a protected path) cannot be applied.
Terminal
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
- CLI
- MCP
- REST
- Web UI
kiwifs refactor has three subcommands. apply and revert show the plan and ask for confirmation unless you pass --yes.Terminal
--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.moves — files, folders, globs, regular expressions
moves — files, folders, globs, regular expressions
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.merges — fold a duplicate into a canonical page
merges — fold a duplicate into a canonical page
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.edits — change frontmatter and text of selected pages
edits — change frontmatter and text of selected pages
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 exampleyear(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.
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.split — one page per heading
split — one page per heading
handbook#heading follow their section, and each section’s own links are re-resolved from its new folder.relinks — repair moves made outside KiwiFS
relinks — repair moves made outside KiwiFS
kiwi_rename with update_links: false — are logged when anything referenced the old path. relinks respells those references without moving anything. Folders work too. GET /api/kiwi/refactor/raw-moves (or the resource kiwi://refactor/raw-moves) builds this manifest for you.delete_empty_dirs and now
delete_empty_dirs and now
delete_empty_dirs: trueremoves 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
kiwi_refactor_script or POST /api/kiwi/refactor/script (body {"script": "..."}); the response holds manifest, output (what the script printed), and plan.
Guardrails
Standing rules in config.toml
Standing rules in config.toml
.kiwi/config.toml
Agent approval
Agent approval
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/pendinguntil a person approves it — in the web UI’s Restructure view, or withPOST /api/kiwi/refactor/approve. An approval lets the agent apply that exact digest once within the hour.
428 when approval is required.Integrity report
Integrity report
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.Transactional apply
Transactional apply
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
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.
Related
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.