1
Plan
Send the manifest to
POST /refactor/plan. Nothing is written. Review the moves, link edits, errors, and warnings, and keep the digest.2
Approve (agents only)
If an agent’s plan exceeds the workspace’s size limits, a human reviews it in
GET /refactor/pending and approves it by digest.3
Apply
Send the same manifest and the digest to
POST /refactor/apply. If any affected file changed since you reviewed, nothing is written and you get the fresh plan back.4
Revert
Pass the returned
commit to POST /refactor/revert to undo it as a new commit.The manifest
A manifest is YAML or JSON. Unknown keys are an error, so a typo can’t silently drop part of a restructuring. It needs at least one ofmoves, merges, edits, rename_tags, relinks, or split.
manifest.yaml
moves
moves
Each entry has
from (a file, a folder ending in /, or a glob) or from_regex, plus to. A to ending in / is a destination folder that keeps each file’s name.merges
merges
from is deleted and everything that pointed at it now points at into. The body of into is left as it is, so write the combined page first, then merge.edits
edits
select picks pages by paths, glob, and/or query (a DQL WHERE expression). At least one criterion is required; use glob: "**" for everything. Then, in order: when (an expression that filters the selection), set, compute (keys set from expressions such as slug(title)), unset, add_tags, remove_tags, replace (find, with, optional regex), and move_to (a destination template such as archive/{{year}}/{{slug}}.md). on_conflict is error (default) or suffix("-%d"). force lets the edit change frontmatter keys owned by another field manager. Selection always sees paths as they were before the manifest.relinks
relinks
Repairs references after a move made outside KiwiFS (a WebDAV or NFS rename, a shell
mv). Same shape as moves. See raw moves.split
split
Breaks a page into one page per heading. Fields:
from, level (default 2), into (default: a folder named after the page), and confirm. A split stays plan-only until confirm: true.delete_empty_dirs and now
delete_empty_dirs and now
delete_empty_dirs: true removes folders the plan leaves empty. now pins the clock that when, compute, and move_to expressions see; pass back the plan’s now to apply exactly what you reviewed.[refactor] section of .kiwi/config.toml: approval_files and approval_links (agent plans above these sizes need approval; 0 means the defaults of 50 files and 200 links, -1 turns the check off), protect (globs no plan may touch), and scope (the only folder plans may touch). Plans also refuse to move pages another actor has claimed.
Preview a plan
Build the plan for a manifest without writing anything.object
required
The request body is the manifest itself, as YAML or JSON (1 MB limit).
boolean
default:"false"
Add a
diffs array with a unified diff for every written page.string
workspace-edit returns the plan as an LSP WorkspaceEdit instead, for editors.string
Who will apply the plan. Pages claimed by anyone else can’t be moved.
string
default:"rest"
Names the calling tool in the plan and the commit trailers. Values starting with
mcp are treated as agents for approval.400 if the manifest can’t be parsed or is empty. A plan with problems still returns 200; check errors. With format=workspace-edit, a plan with errors returns 422 with error and plan.
string
Identifies this exact plan. Pass it to apply.
object[]
Every path that moves, as
from and to.object[]
Every merged page, as
from and to.object[]
Pages whose links are rewritten.
object[]
Pages changed by
edits or rename_tags: path, from, and a changes list.object[]
Comment files migrated, as
from and to.object[]
Personal-state files updated:
file, changes, conflicts.string[]
Folders removed because they end up empty.
object[]
Each split page (
from) and the pages it becomes.string[]
Problems that block apply. A plan with errors can’t be applied.
object[]
The subset of errors that concern one page (
path, error). Drop those pages and plan again to proceed.object[]
Destinations that can’t be used, by
code: exists, many_to_one, case_or_normalization, forbidden_chars, too_long, trailing_dot_or_space, empty_name, dir_cycle, parent_is_file, or is_folder. Each entry has path, detail, and optionally from and with.string[]
Why a valid plan can’t be applied yet, such as an unconfirmed split.
string[]
Non-blocking issues worth reading before applying.
object
For agent callers:
required and reason.object
Link health after the plan:
unresolved (links that already pointed nowhere), orphaned (pages that lose all inbound links), and complete (false when the plan didn’t read every page).string
The clock the manifest’s expressions used, if any.
string
Git
HEAD when the plan was built.object
Counts:
pages, links, rewritten, unresolved.Apply a plan
Rebuild the plan for a manifest and apply it as one commit.object
required
The same manifest you planned, as JSON.
string
Digest of the plan you reviewed. If set and the workspace changed since, nothing is written. Strongly recommended.
boolean
For agent clients: the user confirmed this plan (for example through MCP elicitation).
string
Commit author.
string
default:"rest"
Calling tool. Values starting with
mcp must have approval for plans over the workspace’s limits.object
What changed.
object
The plan that was applied.
Revert a commit
Undo a commit as a new commit. Every page the commit changed goes back to its previous bytes, and comment and personal-state files a restructuring rewrote go back too. It works on any commit, not only refactors.string
required
Commit ID to revert.
boolean
default:"false"
Return the revert plan without applying it.
string
Digest of the revert plan you reviewed with
dry_run.string
Commit author.
dry_run, the response is the revert plan (same shape as a plan, with reverts set to the commit). Otherwise it is applied and plan, as for apply.
Returns 400 for a missing or invalid commit ID, 409 if the digest no longer matches or a file changed while applying, 422 if any affected file changed since the commit (a revert never discards later work), and 501 when git versioning is off.
Approve a plan for an agent
Record a human’s approval of a plan so an agent can apply it once within the next hour, even though it exceeds the workspace’s approval limits.object
required
The manifest being approved.
string
required
Digest of the plan being approved.
400 if either field is missing, and 409 with the fresh plan if the plan has errors or no longer produces that digest.
List plans waiting for approval
Return the plans agents tried to apply that exceed the workspace’s approval limits.object[]
Compute a manifest with a script
Run a Starlark (Python-like) program that returns a manifest, for selection logic too irregular for globs, DQL, and expressions. Nothing is written; apply the result with apply.string
required
Starlark source. Return the manifest from
main() or set a global manifest. Available functions: query(dql), files(glob="**"), read(path), frontmatter(path), links(path), slug(text), json.encode, json.decode, and print (captured).string
Who will apply the plan.
while loops or recursion, and is bounded by steps, memory (256 MB), and time (30 seconds). Returns 400 for an empty script and 422 if the script fails or returns an invalid manifest.
object
The manifest the script returned.
string
What the script printed.
Plan an editor’s file renames
Turn an LSPWorkspaceEdit (an editor’s rename refactoring) into a manifest and plan it. Only the file renames are kept; text edits are ignored and recomputed by the planner.
object[]
LSP document changes.
rename entries (oldUri, newUri) become moves.object
LSP text edits by URI. Ignored.
400 if a rename points outside the knowledge base or the edit renames no files.
object
The manifest built from the renames.
string[]
Parts of the edit that were not used, and why.
List moves that left references behind
Return moves made without relinking (WebDAV or NFS renames, renames with link updates turned off, or files moved on disk while the server was watching) whose old paths are still referenced, plus arelinks manifest that repairs them. Plan and apply that manifest like any other.
object[]
object
A manifest with
relinks that repairs the references.List broken links
Return every wiki link that doesn’t resolve to an existing file, with the best fuzzy match when there is one.object[]
integer
Number of broken links.
List orphan pages
Return pages that no other page links to.Fix broken links
Rewrite broken wiki links to their best fuzzy match: an exact path, then a path-suffix match (ignoring numeric folder prefixes such as06-), then a matching file name. Links with no match are left alone.
boolean
default:"false"
Report the fixes without writing. Also accepted as a
dry_run=true query parameter when there is no JSON body.string
Commit author.
object[]
Each fix:
source, old_target, new_target.integer
Number of fixes.
boolean
Whether anything was written.
MCP equivalents
kiwi_refactor_plan, kiwi_refactor_apply, kiwi_refactor_script, and kiwi_refactor_revert cover plan, apply, script, and revert. Over MCP, plans larger than the workspace’s limits ask the user to confirm, or wait for approval in the web UI if the client can’t ask. Approve, pending, raw moves, workspace edits, and the link-health endpoints are REST-only.
Related
Wiki links
How links resolve, and why moves keep them working.
Files
Single-file rename and directory rename.
Claims
Claimed pages can’t be moved by other actors.
MCP tools
The refactor tools agents use.