Save

Save#

Save stages a complete replacement beside the destination before publishing it. A failed write or close leaves the original intact. Existing permission bits and symlink targets are preserved; hard-linked destinations are refused rather than silently splitting aliases. Replacement requires a writable parent directory. On POSIX, staging requires a parent owned by the current user with no group or other write bits (including sticky shared directories); otherwise another user could substitute the staging name. This check applies to new files too. It does not protect against same-user concurrent writers or hostile ancestor changes. Replacement creates a new inode. Files with extended metadata (including POSIX ACLs), inherited default ACLs on an existing target’s parent, or ownership that cannot be retained are refused rather than silently losing security metadata. On Windows, overwriting an existing file is refused before staging: Python’s permission-bit APIs cannot preserve its security descriptor, and staging under the parent’s inherited DACL could expose restricted contents. Creating a new file remains supported. Existing-file replacement is also refused on macOS: ACL security metadata is not reliably visible through listxattr(), so an empty attribute listing cannot prove that replacing the inode is safe. Native ACL preservation is not implemented. New-file creation remains supported. On Linux, a filesystem reporting unsupported extended attributes is treated as having none; permission and I/O errors during inspection still fail closed. Platforms without an extended-attribute inspection API refuse existing-file replacement. This is not a power-loss durability or concurrent-writer guarantee. Append retains its separate in-place behavior.

Gives the assistant the ability to save whole files, or append to them.

Instructions

Create or overwrite a file with the given content.

The path can be relative to the current directory, or absolute.
If the current directory changes, the path will be relative to the new directory.

### When to use save vs patch

Use `save` for new files, full rewrites, or edits that touch most of a file.
Use `patch` for targeted edits to existing files; it keeps surrounding content intact.

Examples

User
write a hello world script to hello.py
Assistant
save hello.py
print("Hello world")
System
Saved to `hello.py`
User
make it all-caps
Assistant
save hello.py
print("HELLO WORLD")
System
Saved to `hello.py`

Instructions

Append the given content to a file.

Examples

User
append a print "Hello world" to hello.py
Assistant
append hello.py
print("Hello world")
System
Appended to `hello.py`
gptme.tools.save.check_for_placeholders(content: str) → bool

Check if content contains placeholder lines.

gptme.tools.save.execute_append(code: str | None, args: list[str] | None, kwargs: dict[str, str] | None) → Generator[Message, None, None]

Append code to a file.

gptme.tools.save.execute_append_impl(content: str, path: Path | None) → Generator[Message, None, None]

Append with save hooks: PRE receives the request, POST the written chunk.

gptme.tools.save.execute_save(code: str | None, args: list[str] | None, kwargs: dict[str, str] | None) → Generator[Message, None, None]

Save code to a file.

gptme.tools.save.execute_save_impl(content: str, path: Path | None, *, baseline: _SaveBaseline | None = None) → Generator[Message, None, None]

Actual save implementation.

gptme.tools.save.preview_append(content: str, path: Path | None) → str | None

Prepare preview content for append operation.

gptme.tools.save.preview_save(content: str, path: Path | None) → str | None

Prepare preview content for save operation.