mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-10-01 02:04:21 +00:00
* feat(config): add comprehensive job definitions for vault operations - Add utility jobs like version, search, traverse, list, read, stat - Include file operations like move, delete, upload, download - Add daily workspace management jobs: daily_list, daily_resolve, daily_reindex - Update descriptions to reflect vault-based operations instead of working_dir - Add proper section headers and documentation for each job category refactor(steps): reorganize step modules and remove demo steps - Move steps into categorized packages: common, crud, frontmatter, daily, jobs - Remove demo steps (DemoEchoStep1, DemoEchoStep2, StreamDemoStep1, StreamDemoStep2) - Add new steps: InitStep for vault initialization, TraverseStep for graph traversal - Update __init__.py to auto-import all step modules - Organize imports by functionality (common, CRUD operations, frontmatter, daily) feat(vault): implement vault-centric file operations and configuration - Change default config to use vault_dir instead of working_dir - Add environment variable support for embedding configuration - Implement file watcher with lite backend for daily/digest directories - Update search step to use 'name' instead of 'title' from frontmatter - Create ResourceEntry schema for tracking uploaded assets docs(steps): add comprehensive documentation for all step categories - Document file-I/O split by blast radius (crud vs frontmatter packages) - Add detailed descriptions for each step category and functionality - Explain the purpose and usage patterns for different types of file operations - Provide clear parameter documentation for all new job configurations * fix(config): correct vault directory path and remove unused job configurations - Fix vault_dir from 'vaultd' to 'vault' in default configuration - Remove deprecated traverse and list job configurations - Remove unused tag tooling configurations - Remove background watch_file job configuration refactor(steps): remove unused jobs module import - Comment out jobs module import in steps/__init__.py - This removes unused synchronizer and digester step registrations refactor(tests): update import path and add pylint directive - Update ResourceEntry import from reme4.schema to reme4.schema.resource_meta - Add pylint disable directive for unused argument in test datetime mocks * efactor(steps): remove unused modules from __all__ - Remove "background" module from __all__ list - Remove "jobs" module from __all__ list - These modules were no longer being used in the steps package * feat(config): update vault directory structure and remove file watcher - Change vault_dir reference from ./vault to ./vault in CLI example - Add daily_dir, digest_dir, and resource_dir configuration options - Remove file_watcher component configuration as it's no longer needed - Update comment to reflect correct module name (reme4vault) refactor(steps): add background step and remove deprecated init step - Import and register background step module - Remove deprecated InitStep from common steps - Update __all__ export list to include background step refactor(reindex): improve reindex step to scan vault directly - Update docstring to reflect vault scanning instead of watcher sync - Replace file watcher stop/start logic with direct vault path walking - Add support for suffix filtering during reindex operation - Use index_changes job to process found files refactor(wikilink_utils): enhance inbound source lookup with link scope - Import LinkScopeEnum for proper type handling - Update get_inlinks call to use ALL scope for virtual targets - Improve documentation for reverse-index lookup behavior test(refactor): clean up test suite removing deprecated functionality - Remove test_init_job and test_demo_job unit tests - Update help job assertion to check for literal command format - Change test directory from .reme to vault in CRUD tests - Remove init and demo job calls from integration test BREAKING CHANGE: Removes file_watcher component and init step * style(steps): fix import formatting in __init__.py Add proper spacing in the background module import statement to maintain consistent code style and readability. * refactor(config): change default vault directory from vault to .reme Default dev config now points vault_dir at ./.reme so `python -m reme4 start` can be run from the repo root and exercise the full atomic-tool surface against the seeded test data. BREAKING CHANGE: The default vault directory has been changed from 'vault' to '.reme' in the configuration. * docs(reme4_report): fix markdown formatting and remove extra content * refactor(file_parser): delegate wikilink extraction to WikilinkHandler * fix(search): handle empty query case gracefully - Replace assertion with conditional check for empty query - Set response success to false when query is empty - Return error message instead of throwing assertion error - Maintain existing validation for other parameters
133 lines
5.3 KiB
Python
133 lines
5.3 KiB
Python
"""``file_delete`` — hard-delete a file or folder under the vault (reports inbound refs).
|
|
|
|
Removes the file (or folder tree) from disk; the watcher then prunes
|
|
the affected chunks from all projections (vector / keyword / file_graph).
|
|
For **soft** deletion that keeps a file addressable, use
|
|
``property_update(status=archived)`` instead — that's the path
|
|
structure.md's Decay algorithm is designed around.
|
|
|
|
Reference reporting (no auto-fix). Before deleting, the inbound
|
|
wikilink count for each doomed ``.md`` file is captured from the
|
|
file_graph's reverse index (literal full-path matching, same rule
|
|
as the wikilink retarget helper). For folder deletes, sources that live inside
|
|
the same folder are filtered out — those links die alongside the
|
|
delete and can't surface as dangling. The remaining inbound list
|
|
is the agent's punch list for follow-up rewrites.
|
|
|
|
The delete itself is unconditional: this step does not rewrite
|
|
inbound references on the agent's behalf because there is no
|
|
canonical "new target" — the agent decides per-reference whether
|
|
to rewrite (via ``file_move`` to a merge target), edit the
|
|
citing prose, or accept dangling.
|
|
"""
|
|
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
from ..base_step import BaseStep
|
|
from ...utils.wikilink_handler import WikilinkHandler
|
|
|
|
from ...components import R
|
|
|
|
|
|
def _is_inside(rel: str, folder_rel: str) -> bool:
|
|
"""``rel`` is the same as or nested under ``folder_rel`` (relative to the vault)."""
|
|
prefix = folder_rel.rstrip("/") + "/"
|
|
return rel == folder_rel or rel.startswith(prefix)
|
|
|
|
|
|
@R.register("delete_step")
|
|
class DeleteStep(BaseStep):
|
|
"""Hard-delete the path at ``path`` (file or folder, relative to the vault)."""
|
|
|
|
async def execute(self):
|
|
assert self.context is not None
|
|
path: str = self.context.get("path", "") or ""
|
|
assert path, "path is required"
|
|
payload = await self._delete(path)
|
|
if "error" in payload:
|
|
self.context.response.success = False
|
|
self.context.response.answer = f"Error: {payload['error']}"
|
|
elif payload.get("is_dir"):
|
|
self.context.response.success = True
|
|
self.context.response.answer = f"Deleted directory {path} ({len(payload['deleted_files'])} file(s))"
|
|
else:
|
|
self.context.response.success = True
|
|
self.context.response.answer = f"Deleted {path}"
|
|
self.context.response.metadata.update(payload)
|
|
|
|
async def _delete(self, path: str) -> dict:
|
|
if not path:
|
|
return {"path": path, "error": "not found"}
|
|
vault_dir = Path(self.file_store.vault_path or ".").resolve()
|
|
target = (vault_dir / path).resolve()
|
|
if target.is_file():
|
|
inbound = await WikilinkHandler.find_inbound(self.file_store, target=path)
|
|
target.unlink()
|
|
return {
|
|
"path": path,
|
|
"deleted": True,
|
|
"is_dir": False,
|
|
"deleted_files": [path],
|
|
"inbound": {
|
|
"files_touched": inbound.get("files_touched", 0),
|
|
"links_total": inbound.get("links_total", 0),
|
|
"by_file": inbound.get("by_file", []),
|
|
},
|
|
}
|
|
|
|
if target.is_dir():
|
|
folder_rel = path.rstrip("/")
|
|
deleted_files: list[str] = []
|
|
per_target: list[dict] = []
|
|
external_sources: set[str] = set()
|
|
links_total = 0
|
|
|
|
for md in sorted(target.rglob("*.md")):
|
|
try:
|
|
rel = str(md.relative_to(vault_dir))
|
|
except ValueError:
|
|
continue
|
|
deleted_files.append(rel)
|
|
inbound = await WikilinkHandler.find_inbound(self.file_store, target=rel)
|
|
# Drop sources that also live inside the doomed folder —
|
|
# their links vanish with them and aren't actionable.
|
|
external = [row for row in inbound.get("by_file", []) if not _is_inside(row["path"], folder_rel)]
|
|
if not external:
|
|
continue
|
|
target_total = sum(row["count"] for row in external)
|
|
links_total += target_total
|
|
external_sources.update(row["path"] for row in external)
|
|
per_target.append(
|
|
{
|
|
"target": rel,
|
|
"files_touched": len(external),
|
|
"links_total": target_total,
|
|
"by_file": external,
|
|
},
|
|
)
|
|
|
|
# Also enumerate non-md files for the deleted_files report.
|
|
for entry in sorted(target.rglob("*")):
|
|
if not entry.is_file() or entry.suffix == ".md":
|
|
continue
|
|
try:
|
|
rel = str(entry.relative_to(vault_dir))
|
|
except ValueError:
|
|
continue
|
|
deleted_files.append(rel)
|
|
|
|
shutil.rmtree(target)
|
|
return {
|
|
"path": path,
|
|
"deleted": True,
|
|
"is_dir": True,
|
|
"deleted_files": sorted(deleted_files),
|
|
"inbound": {
|
|
"files_touched": len(external_sources),
|
|
"links_total": links_total,
|
|
"by_target": per_target,
|
|
},
|
|
}
|
|
|
|
return {"path": path, "error": "not found"}
|