This commit is contained in:
Ziyang Guo 2026-09-25 11:05:11 +08:00 • committed by GitHub
commit 9d9a2cfd20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 36 additions and 6 deletions

View file

@ -1,9 +1,9 @@
"""``file_stat`` — peek at file metadata under the workspace without copying it.
Cheap inspection alternative to ``file_download``: the agent gets
size, mtime, mime type, and (for markdown files) the parsed
frontmatter — enough to decide whether to download / parse / skip
without paying the copy cost.
size and mtime in the answer, with mime type and (for markdown files)
the parsed frontmatter alongside in metadata — enough to decide whether
to download / parse / skip without paying the copy cost.
Returns a uniform envelope:
@ -68,12 +68,13 @@ class StatStep(BaseStep):
return
st = target.stat()
mtime = datetime.fromtimestamp(st.st_mtime).isoformat()
payload: dict = {
"path": path,
"absolute_path": str(target),
"exists": True,
"type": "dir" if target.is_dir() else "file",
"mtime": datetime.fromtimestamp(st.st_mtime).isoformat(),
"mtime": mtime,
"ctime": datetime.fromtimestamp(st.st_ctime).isoformat(),
}
if target != original_target:
@ -90,9 +91,9 @@ class StatStep(BaseStep):
except Exception:
meta = {}
payload["frontmatter"] = meta
answer = f"stat: {path} (file, {st.st_size} bytes)"
answer = f"stat: {path} (file, {st.st_size} bytes, mtime {mtime})"
else:
answer = f"stat: {path} (dir)"
answer = f"stat: {path} (dir, mtime {mtime})"
self.context.response.success = True
self.context.response.answer = answer

View file

@ -150,6 +150,35 @@ def test_stat_directory_fallback():
asyncio.run(run())
def test_stat_answer_reports_size_and_mtime():
"""The answer carries size and mtime, not just the path and type.
The ``stat`` job description advertises "size, mtime, exists, is_dir,
is_file", but only ``answer`` reaches an MCP caller — mtime used to live
solely in metadata, so an agent could not tell how fresh a file was.
"""
async def run():
with tempfile.TemporaryDirectory() as tmp, temp_chdir(tmp):
store = await _make_store({"topics/n.md": "---\nname: T\n---\nbody"})
step = crud_stat.StatStep(file_store=store)
await step(path="topics/n.md")
answer = step.context.response.answer
mtime = _metadata(step)["mtime"]
assert answer.startswith("stat: topics/n.md (file, ")
assert "bytes, mtime " in answer
assert answer.endswith(f"{mtime})")
(Path(tmp) / "topics").mkdir(parents=True, exist_ok=True)
step = crud_stat.StatStep(file_store=store)
await step(path="topics")
answer = step.context.response.answer
assert answer == f"stat: topics (dir, mtime {_metadata(step)['mtime']})"
await store.close()
asyncio.run(run())
def test_stat_directory_wins_over_same_name_index():
"""stat prefers an existing directory over its same-name ``.md`` index.