ReMe/reme4/steps/crud/write.py
Sen Huang 7d0bec60be
Some checks failed
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Tests ReMe / Unit Tests - py3.10 (push) Has been cancelled
Tests ReMe / Unit Tests - py3.13 (push) Has been cancelled
feat: rename working_dir to vault_dir and update documentation (#254)
* feat: rename working_dir to vault_dir and update documentation

- Rename working_dir to vault_dir across the application
- Update documentation to reflect vault_dir instead of working_dir
- Change FileFrontMatter title field to name field
- Update .gitignore to include vault directory
- Modify file path descriptions to reference vault instead of working_dir
- Update related configuration and property names accordingly

* refactor(steps): rename working_path to vault_path in CRUD operations

- Rename parameter from `working_path` to `vault_path` in `resolve_path` function
- Update all usages in append, edit, read, and write steps to use `self.vault_path`
- Update documentation comments to reflect the new parameter name
- Update docstring in read.py to mention `vault_dir` instead of `vault`

test(chunked_file_parser): update frontmatter field from title to name

- Change frontmatter field from `title` to `name` in test cases
- Update comment in background steps test to reference `vault_path` instead of `working_path`

* refactor(schema): remove unused ResourceEntry import

* feat(file_graph): add link scope filtering to get_inlinks/get_outlinks

* feat(file-store): add scope parameter to link methods
2026-05-22 18:10:29 +08:00

90 lines
3.4 KiB
Python

"""Write a markdown file with a small, fixed front matter (``name``, ``description``)."""
import frontmatter
from ._file_io import NON_MD_WARNING, detect_file_encoding, gate_md, resolve_path, write_file_safe
from ..base_step import BaseStep
from ...components import R
@R.register("write_step")
class WriteStep(BaseStep):
"""Write (create or overwrite) a markdown file. When the target already exists,
its contents are replaced and a system notice is appended to the answer.
Front matter is restricted to two string fields: ``name`` and ``description``.
The CLI schema declares them as required, but the step itself is lenient —
missing or empty values are silently skipped so manual invocations don't
fail catastrophically."""
def _fail(self, message: str, **meta) -> None:
assert self.context is not None
self.context.response.success = False
self.context.response.answer = f"Error: {message}"
if meta:
self.context.response.metadata.update(meta)
async def execute(self): # pylint: disable=too-many-return-statements
assert self.context is not None
raw = str(self.context.get("path") or "")
content = self.context.get("content")
content = "" if content is None else str(content)
target, err = resolve_path(self.vault_path, raw)
if err:
self._fail(err)
return None
target, is_md = gate_md(target)
existed = target.exists()
# Non-markdown files have no frontmatter convention: name/description
# are silently dropped and the body is written verbatim.
if is_md:
meta: dict = {}
for key in ("name", "description"):
value = self.context.get(key)
if value is None:
continue
s = str(value).strip()
if not s:
continue
meta[key] = s
if meta:
post = frontmatter.Post(content, **meta)
body = frontmatter.dumps(post)
else:
body = content
if not body.endswith("\n"):
body += "\n"
else:
body = content
# Preserve the existing file's encoding when overwriting (e.g. GBK CSV
# stays GBK). New files are written as UTF-8.
encoding = await detect_file_encoding(target) if existed else "utf-8"
try:
await write_file_safe(target, body, encoding=encoding)
except Exception as e: # pylint: disable=broad-except
self._fail(f"write failed: {e}", path=str(target))
return None
try:
nbytes = len(body.encode(encoding))
except (UnicodeEncodeError, LookupError):
nbytes = len(body.encode("utf-8"))
self.context.response.success = True
if existed:
answer = f"Wrote {target} ({nbytes} bytes) " f"[system notice: target already existed and was overwritten]"
else:
answer = f"Wrote {target} ({nbytes} bytes)"
if not is_md:
answer = f"{answer} [system notice: {NON_MD_WARNING}]"
self.context.response.answer = answer
self.logger.info(
f"[{self.name}] wrote path={target} bytes={nbytes} encoding={encoding} "
f"overwritten={existed} is_md={is_md}",
)
return self.context.response