ReMe/reme2/mcp/test/__main__.py
huangsen dd2de16481 ```
feat(file_watcher): add directory deletion support with descendant indexing

Add support for deleting entire directories and their indexed descendants
in the file watcher. Previously only individual file deletions were
handled properly. Now when a directory is deleted, the system finds all
indexed files beneath that directory path and removes them along with
their metadata and chunks.

The implementation includes:
- New `_descendant_indexed_paths` method to find all indexed files
  under a given directory path
- Updated `_on_deleted` method to process both the target path and
  all its indexed descendants
- Proper handling of symlinks and path resolution differences
- Enhanced logging to show directory deletion with child count

Also adds necessary os import for path operations.

refactor(config): restructure configuration profiles for clarity

Rename curated.yaml to remove outdated configuration file and
rename full.yaml to expert.yaml with updated documentation.
Add new service.yaml configuration profile that provides a
service-aligned MCP surface with three main tools:
- retrieve: graph-aware hybrid retrieval
- remember: single write entry point with log/distill modes
- maintain: vault hygiene sweep

The expert configuration now excludes the ingest tool since
cold-path operations are handled by external agents, and adds
memory_lint tool for structural issue detection.

Updated documentation to clarify the different configuration
profiles and their intended usage patterns.
```
2026-05-11 10:52:25 +08:00

77 lines
2.4 KiB
Python

"""CLI runner: `python -m reme2.mcp.test`
Boots one Application per profile against a fresh temp vault, runs the
profile's CHECKS list in order, prints a per-check status line plus a
final summary. Exit code is the number of failed checks across both
profiles.
Per-profile suites are intentionally sequential — later checks rely on
side-effects from earlier ones (e.g. `sync.append` after `sync.create`).
Cross-profile work happens in independent vaults.
"""
from __future__ import annotations
import asyncio
import shutil
import sys
import traceback
from . import test_expert, test_service
from ._helpers import AppContext, make_context
SUITES = {
"expert": test_expert.CHECKS,
"service": test_service.CHECKS,
}
async def _run_suite(profile: str, checks: list[tuple[str, callable]]) -> tuple[int, int]:
"""Run one suite. Returns (passed, total)."""
print(f"\n========== profile: {profile} ==========")
ctx: AppContext | None = None
tmp = None
passed = 0
try:
ctx, tmp = await make_context(profile)
print(f" bootstrapped: vault={ctx.vault}, jobs={len(ctx.jobs)}, "
f"file_store={len(ctx.file_store)}")
for label, fn in checks:
try:
summary = await fn(ctx)
print(f"{label:32s} {summary}")
passed += 1
except Exception as e:
print(f"{label:32s} {type(e).__name__}: {e}")
traceback.print_exc()
except Exception as e:
print(f" ✗ bootstrap failed: {type(e).__name__}: {e}")
traceback.print_exc()
finally:
if ctx is not None:
try:
await ctx.app.close()
except Exception as e: # noqa: BLE001
print(f" (warn) app.close failed: {e}")
if tmp is not None:
shutil.rmtree(tmp, ignore_errors=True)
return passed, len(checks)
async def _main() -> int:
summary: dict[str, tuple[int, int]] = {}
for profile, checks in SUITES.items():
summary[profile] = await _run_suite(profile, checks)
print("\n========== summary ==========")
failed = 0
for profile, (passed, total) in summary.items():
marker = "" if passed == total else ""
print(f" {marker} {profile}: {passed}/{total}")
failed += (total - passed)
return 0 if failed == 0 else failed
if __name__ == "__main__":
sys.exit(asyncio.run(_main()))