Newer
Older
navi-1 / manuals / filesystem.md

filesystem — Manual

What it does

Reads and edits files and directories. The action that matters most is picking the cheapest deterministic way to make a change: an AI-assisted edit is the last resort, never the first.

Editing policy — in this order

  1. edit — exact text. Pass old (which must occur exactly once in the file) and new. Read the file first and copy old verbatim, including indentation. This is the default for almost every change, and it either applies exactly or fails loudly — it cannot quietly edit the wrong line.
  2. edit_lines — line numbers. Pass an operations array when you know the exact lines ("change line 15"). Deterministic, no AI call.
  3. smart_edit — AI-assisted, for changes that genuinely cannot be expressed as text or line numbers: "rename this symbol everywhere", "add type hints to every function". It costs an LLM call and reads the whole file. Try the two above first, always.
  4. write — create a file or rewrite it entirely (pass content).
  5. query — ask a question about a file's content and get an answer instead of the file.
  6. Everything else — read, append, list, find, find_up, grep, diff, info, copy, move, delete, exists, mkdir.

Parameters

Parameter Used by Description
action required One of the actions listed above.
path required File or directory. ~ is expanded.
content write, append Text to write or append.
old / new edit Exact text to replace, and its replacement.
operations edit_lines Array of `{"op": "replace"\ "delete"\ "insert", "start", "end", "after", "content"}. Lines are 1-based and inclusive;insertusesafter`.
destination move, copy, diff Target path, or the second file to compare against. Missing parent directories are created.
pattern find, find_up, grep Glob for find (e.g. *.log), exact filename for find_up, search text for grep.
glob grep Optional filename filter for a recursive search, e.g. *.py.
regex grep true for pattern matching, default false = literal substring.
question query The question to answer from the file.
instruction smart_edit The change to make, in natural language.
offset / limit read First line (1-based) and how many to return.
numbered read Default true, prefixed with 1-based line numbers. Set false for raw content you are about to copy into an edit.
recursive list Full tree instead of the top level.

Actions worth knowing

{"action": "info", "path": "src/app.py"}
{"action": "read", "path": "src/app.py", "offset": 40, "limit": 30}
{"action": "read", "path": "src/app.py", "numbered": false}

{"action": "edit", "path": "src/app.py", "old": "    return None", "new": "    return {}"}
{"action": "edit_lines", "path": "src/app.py",
 "operations": [{"op": "replace", "start": 15, "end": 15, "content": "    limit = 50"}]}

{"action": "grep", "path": "src", "pattern": "TODO", "glob": "*.py"}
{"action": "find", "path": ".", "pattern": "*.log"}
{"action": "find_up", "path": "src/deep/file.py", "pattern": "pyproject.toml"}
{"action": "diff", "path": "src/app.py", "destination": "src/app.py.bak"}
  • info before read on an unknown file — it reports the size, which tells you whether a full read is going to flood the context.
  • find_up walks from path towards the root looking for an exact filename — the way to locate pyproject.toml/.git from a nested directory.
  • diff is a unified diff between two files — path against destination (both required; directories are rejected).

query and smart_edit

Both spend an LLM call, so both are for what the deterministic actions cannot do.

query answers a question instead of returning the file — use it to avoid reading a large file whose content you only need one fact from:

{"action": "query", "path": "src/app.py", "question": "What does calculate() return?"}
{"action": "query", "path": "src/app.py", "question": "Where is class UserManager defined?"}
{"action": "query", "path": "deploy.sh", "question": "Which environment variables does this read?"}

smart_edit takes an instruction in natural language:

{"action": "smart_edit", "path": "src/app.py", "instruction": "Rename process to handle_request"}
{"action": "smart_edit", "path": "src/app.py", "instruction": "Add type hints to every function"}
{"action": "smart_edit", "path": "src/app.py", "instruction": "Replace the hardcoded URL with a constant BASE_URL"}

It reads the whole file and can touch more than you asked. When the change is expressible as exact text or line numbers, edit/edit_lines do it exactly, for free, and fail loudly instead of approximately.

Access

In multi-user mode every path resolves inside one of two roots: user_data/<user_id>/ and the current session directory (session_files/{session_id}/, the path is in the session context). A path outside both returns Access denied: ... outside allowed paths. Relative paths always resolve against user_data/<user_id>/. Do not work around a refusal by copying files into your sandbox or writing to /tmp — if the file you need is elsewhere, ask the user to upload it. In single-user/admin mode the allowed roots are the working directory and the configured paths.

Common mistakes

  • edit with an old string that appears twice — the call is refused, correctly. Include more surrounding context until it is unique.
  • old copied from a numbered read — the line-number column is not part of the file. Read with numbered: false when copying text into an edit.
  • write to change one line: it replaces the whole file, and anything you did not retype is gone. Use edit.
  • smart_edit for something edit could do — an LLM call to change a line is a waste, and it can touch more than you asked.
  • delete on a directory — it removes the whole tree recursively, with no prompt and no undo.
  • mkdir for a path whose parent does not exist: it does not create intermediate directories. write does.