Newer
Older
navi-1 / manuals / filesystem_write.md

filesystem_write — Manual

What it does

Creates, edits and manages files and directories. Reading and searching live in filesystem_read; LLM-backed edits live in file_ai.

Editing policy — in this order

  1. edit — exact text. old must occur exactly once in the file; new replaces it. Read the file first and copy old verbatim, including indentation. This is the default for almost every change: it either applies exactly or fails loudly.
  2. edit_lines — line numbers. Use operations when you know the exact lines. Lines are 1-based and inclusive. Deterministic, no AI call.
  3. insert — add text after a known line number (after: 0 means before line 1). A simpler form of one insert operation.
  4. write — create a file, or rewrite it entirely. It replaces everything you did not retype.
  5. file_ai edit — only when the change cannot be expressed as exact text or line numbers (for example, "rename this symbol everywhere").

Actions

Action Use it for
write create a file or replace its whole content
append add text to the end of a file
edit exact-once text replacement (old → new)
edit_lines one or more line-based operations
insert insert text after line after
mkdir create a directory and any missing parents
move move a file or directory
copy copy a file or directory
delete delete a file or an empty directory; recursive: true for a non-empty tree

Edit

{"action": "edit", "path": "src/app.py", "old": "    return None", "new": "    return {}"}
{"action": "edit", "path": "src/app.py", "old": "process", "new": "handle", "replace_all": true}

If old is not found, the error shows the closest lines with their numbers and tells you to copy the exact text (or to drop the line-number column if you copied a numbered read). If it occurs more than once, the error lists the line numbers; include more surrounding context, use edit_lines, or set replace_all: true deliberately.

A successful edit returns the changed lines with a little context, so you can verify the result without another read. The full diff is shown in the UI.

Line edits

edit_lines takes an array of operations. Each operation is one of:

{"op": "replace", "start": 15, "end": 15, "content": "    limit = 50"}
{"op": "delete",  "start": 20, "end": 22}
{"op": "insert",  "after": 30, "content": "    # new section"}
  • start/end are 1-based and inclusive.
  • insert uses after: 0 inserts before line 1, len(lines) appends.
  • Ranges must not overlap; merge them into one operation instead.
  • The result includes a numbered unified diff.

For a single insertion, insert is shorter:

{"action": "insert", "path": "src/app.py", "after": 15, "content": "    # note"}

Writing and safety

  • write creates missing parent directories. On an existing file it reports the old and new line counts; if the new content is less than half the old, it warns that the file may have been truncated.
  • write with empty content over an existing file is refused unless you pass force: true.
  • move and copy refuse to replace an existing destination unless you pass overwrite: true.
  • delete refuses a non-empty directory unless you pass recursive: true.
  • mkdir creates intermediate directories.
{"action": "write", "path": "out/report.md", "content": "# Report\n"}
{"action": "mkdir", "path": "out/parts"}
{"action": "copy", "path": "a.scad", "destination": "b.scad"}
{"action": "move", "path": "old.txt", "destination": "archive/old.txt", "overwrite": true}
{"action": "delete", "path": "build", "recursive": true}

Access

In multi-user mode every path resolves inside user_data/<user_id>/ or the current session directory. A path outside both returns Access denied. Do not work around a refusal by copying files into the sandbox or /tmp; ask the user to upload the file.

Common mistakes

  • edit with an old string that appears twice — the call is refused, correctly. Include more surrounding context, or set replace_all: true only when you really mean every occurrence.
  • old copied from a numbered read — the 12: column is not part of the file.
  • write to change one line — it replaces the whole file. Use edit.
  • edit_lines ranges that overlap — merge them into one operation.
  • delete on a directory without recursive: true — it refuses; this is a guard, not a failure to retry blindly.