Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

llm-tools-edit

PyPI Changelog Tests License

LLM plugin providing tools for editing files. Gives LLMs the ability to create, view, and modify files using three interchangeable backends.

Installation

Install this plugin in the same environment as LLM.

llm install llm-tools-edit

Backends

This plugin provides three Toolbox backends. Each provides the same five tools:

Tool Description
view View file contents with line numbers, or list a directory
str_replace Replace an exact string in a file (must appear exactly once)
create Create a new file (errors if it already exists)
insert Insert text after a given line number
list_files List all files

Filesystem

Edit real files on disk, scoped to a specific directory. All paths are relative to the root directory, and path traversal outside the root is blocked.

llm -T 'Filesystem("/tmp/myproject")' "Create a Python hello world app"

The path argument is the root directory. All file operations are restricted to this directory and its subdirectories.

MemoryFS

An in-memory virtual filesystem. Files exist only in memory and are discarded when the process exits. State persists across tool calls within a single conversation, so an LLM agent can create files and iteratively edit them without touching disk.

llm -T MemoryFS "Create a Flask app with two routes"

No arguments required. Paths use absolute format (e.g. /src/main.py).

SQLiteFS

Store files in a SQLite database table. The table is created automatically if it doesn't exist. State persists across sessions — you can come back later and continue editing.

llm -T 'SQLiteFS(db_path="demo.db", table_name="files")' "Create a hello world app"

Arguments:

  • db_path — path to the SQLite database file
  • table_name — name of the table to store files in (schema: path TEXT PRIMARY KEY, content TEXT)

Output truncation

Large files and single-line blobs (e.g. minified JS) can overwhelm a model's context window. All three backends accept optional max_lines and max_chars keyword arguments that truncate view output and append a message telling the model how to see more:

llm -T 'Filesystem("/tmp/myproject", max_lines=200, max_chars=20000)' "Fix the bug in app.py"
fs = MemoryFS(max_lines=200, max_chars=20000)
sqlfs = SQLiteFS("demo.db", "files", max_lines=200, max_chars=20000)
  • max_lines — truncate after this many lines. A footer like [Showing lines 1-200 of 5,432 total. Use view_range to see more.] is appended.
  • max_chars — truncate after this many characters of formatted output. Catches the degenerate case of a single 2 MB line with no newlines. A footer like [Truncated: output exceeded 20,000 character limit. ...] is appended.

Both limits are off (None) by default. When both are set, whichever triggers first takes effect — and both footers appear if both trigger. The model can always use view_range to read specific sections of a large file without hitting the limits.

Tool details

view

View a file's contents with line numbers, or list a directory's entries.

The optional view_range parameter accepts a comma-separated start and end line number (1-indexed). Use -1 for end to mean end-of-file.

Examples: view_range="1,10" shows lines 1-10, view_range="5,-1" shows line 5 to end.

str_replace

Find and replace an exact string in a file. The old_str must appear exactly once in the file — if it appears zero times or more than once, the tool raises an error. This ensures replacements are unambiguous.

create

Create a new file with the given content. Raises an error if the file already exists (to prevent accidental overwrites). For Filesystem, parent directories are created automatically if needed.

insert

Insert text after a given line number. Use insert_line=0 to insert at the beginning of the file.

Library functions

The text-editing primitives used internally by the Toolbox classes are also available as standalone functions. You can import and use these directly in your own code.

view_lines(content, view_range="", *, max_lines=None, max_chars=None)

Format a string as numbered lines. Returns each line prefixed by its 1-indexed line number and a tab.

from llm_tools_edit.lib import view_lines

view_lines("alpha\nbeta\ngamma\n")
# '1:\talpha\n2:\tbeta\n3:\tgamma'

view_lines("alpha\nbeta\ngamma\n", view_range="2,3")
# '2:\tbeta\n3:\tgamma'

view_lines("alpha\nbeta\ngamma\n", view_range="2,-1")
# '2:\tbeta\n3:\tgamma'

The view_range parameter accepts "start,end" (1-indexed). Use -1 for end to mean end-of-file.

The optional max_lines and max_chars parameters truncate output and append a descriptive footer when limits are exceeded:

content = "".join(f"line {i}\n" for i in range(10_000))

view_lines(content, max_lines=3)
# '1:\tline 0\n2:\tline 1\n3:\tline 2\n[Showing lines 1-3 of 10000 total. Use view_range to see more.]'

view_lines("x" * 1_000_000, max_chars=50)
# '1:\txxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n[Truncated: ...]'

str_replace_content(content, path, old_str, new_str)

Replace exactly one occurrence of old_str in content. Raises ValueError if the string is not found or appears more than once, ensuring every replacement is unambiguous. The path argument is used only in error messages.

from llm_tools_edit.lib import str_replace_content

str_replace_content("hello world", "f.py", "hello", "goodbye")
# 'goodbye world'

str_replace_content("aaa\naaa", "f.py", "aaa", "bbb")
# ValueError: old_str appears 2 times in f.py; must be unique

insert_content(content, insert_line, insert_text)

Insert text after a given line number. Use insert_line=0 to insert at the beginning.

from llm_tools_edit.lib import insert_content

insert_content("line1\nline2\n", 1, "new\n")
# 'line1\nnew\nline2\n'

insert_content("line1\nline2\n", 0, "header\n")
# 'header\nline1\nline2\n'

list_dir_from_paths(paths, prefix)

Given a flat list of absolute file paths and a directory prefix, return the set of immediate children — files as bare names, subdirectories with a trailing /. Returns None if nothing matches.

from llm_tools_edit.lib import list_dir_from_paths

list_dir_from_paths(["/src/main.py", "/src/utils.py", "/README.md"], "/")
# {'README.md', 'src/'}

list_dir_from_paths(["/src/main.py", "/src/lib/helper.py"], "/src")
# {'main.py', 'lib/'}

list_dir_from_paths(["/src/main.py"], "/other")
# None

Python API

import llm
from llm_tools_edit import Filesystem, MemoryFS, SQLiteFS

model = llm.get_model("gpt-4.1-mini")

# Real filesystem — scoped to a directory
fs = Filesystem("/tmp/myproject")
result = model.chain(
    "Create a hello world Python app",
    tools=[fs],
).text()

# In-memory filesystem
memfs = MemoryFS()
result = model.chain(
    "Create a Flask app",
    tools=[memfs],
).text()

# SQLite-backed filesystem
sqlfs = SQLiteFS("demo.db", "files")
result = model.chain(
    "Create a hello world app",
    tools=[sqlfs],
).text()

For multi-turn conversations where state persists across prompts:

memfs = MemoryFS()
conversation = model.conversation(tools=[memfs])
conversation.chain("Create /app.py with a Flask hello-world app")
conversation.chain("Add a /goodbye route to the app")
conversation.chain("View the final version of /app.py")

Development

To set up this plugin locally, first checkout the code. Then create a new virtual environment:

cd llm-tools-edit
python -m venv venv
source venv/bin/activate

Now install the dependencies and test dependencies:

llm install -e '.[test]'

To run the tests:

python -m pytest

Release files for llm-tools-edit 0.1a0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for llm-tools-edit 0.1a0
File Size Uploaded
llm_tools_edit-0.1a0.tar.gz 19.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for llm-tools-edit 0.1a0
File Interpreter ABI Platform
llm_tools_edit-0.1a0-py3-none-any.whl Python 3 none any Details

Total release size: 41.0 kB

Release files / llm_tools_edit-0.1a0.tar.gz

Download URL llm_tools_edit-0.1a0.tar.gz
Size 19.9 kB
Tags Source
SHA-256 checksum
How to use checksums
d0420a3d6a574332f3ab542d6df0fc8fe7877760970e02e3be429e2cc71edc00
BLAKE2b-256 checksum
How to use checksums
140fa48eff0b22904b60f1fa6fbe4d896446adb3741d0a8e9c45ec85fa61ddf7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 9, 2026.

Transparency log

Release files / llm_tools_edit-0.1a0-py3-none-any.whl

Download URL llm_tools_edit-0.1a0-py3-none-any.whl
Size 21.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
618bf301ed9b2f7a198b9e56f0042766ac67c34ccd3a73f8d1c92d97ed91c3f2
BLAKE2b-256 checksum
How to use checksums
c0c03cefa074b9215e83a332c4e8b41d038546cdf9883efb479335d9823beae6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 9, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1a0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page