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 |
|
| System |
Saved to `hello.py` |
| User |
make it all-caps |
| Assistant |
|
| System |
Saved to `hello.py` |
Instructions
Append the given content to a file.
Examples
| User |
append a print "Hello world" to hello.py |
| Assistant |
|
| 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.