feat: enhance profile path resolution and add example profiles for better project adaptability
This commit is contained in:
@@ -27,7 +27,9 @@ PROTOCOL_VERSION = "2025-06-18"
|
||||
SERVER_NAME = "aiw-context-mcp"
|
||||
SERVER_VERSION = "0.1.0"
|
||||
LOCAL_ENV = ROOT / "scripts" / "mattermost-proxy" / ".env"
|
||||
INDEX_ROOT = ROOT / ".aiw" / "indexes"
|
||||
AIW_SCRIPT_DIR = ROOT / "scripts" / "aiw"
|
||||
sys.path.insert(0, str(AIW_SCRIPT_DIR))
|
||||
import profile as aiw_profile # noqa: E402
|
||||
|
||||
|
||||
def load_local_env(path: Path = LOCAL_ENV) -> None:
|
||||
@@ -47,22 +49,20 @@ def load_local_env(path: Path = LOCAL_ENV) -> None:
|
||||
|
||||
|
||||
def profile_dir(profile: str) -> Path:
|
||||
if profile == "fidelity":
|
||||
return ROOT
|
||||
candidate = ROOT / "profiles" / profile
|
||||
return candidate if candidate.exists() else ROOT
|
||||
|
||||
|
||||
def knowledge_dir(profile: str) -> Path:
|
||||
base = profile_dir(profile)
|
||||
candidate = base / "project-knowledge"
|
||||
return candidate if candidate.exists() else ROOT / "project-knowledge"
|
||||
return aiw_profile.knowledge_dir(profile, root=ROOT)
|
||||
|
||||
|
||||
def inbox_dir(profile: str) -> Path:
|
||||
base = profile_dir(profile)
|
||||
candidate = base / "ai" / "inbox"
|
||||
return candidate if candidate.exists() else ROOT / "ai" / "inbox"
|
||||
return aiw_profile.inbox_dir(profile, root=ROOT)
|
||||
|
||||
|
||||
def rel(path: Path) -> str:
|
||||
return aiw_profile.relative_to_root(path, root=ROOT)
|
||||
|
||||
|
||||
def mattermost_mirror_dir(profile: str) -> Path:
|
||||
@@ -257,7 +257,7 @@ def project_current_context(args: dict[str, Any]) -> dict[str, Any]:
|
||||
result = []
|
||||
for path in files:
|
||||
if path.is_file():
|
||||
result.append({"path": str(path.relative_to(ROOT)), "text": path.read_text(encoding="utf-8", errors="replace")})
|
||||
result.append({"path": rel(path), "text": path.read_text(encoding="utf-8", errors="replace")})
|
||||
return tool_result({"profile": profile, "canonical": True, "files": result})
|
||||
|
||||
|
||||
@@ -270,8 +270,8 @@ def project_search_memory(args: dict[str, Any]) -> dict[str, Any]:
|
||||
base = knowledge_dir(profile)
|
||||
matches: list[dict[str, Any]] = []
|
||||
for path in sorted(base.rglob("*.md")):
|
||||
rel = path.relative_to(base)
|
||||
if str(rel).startswith("09-templates/"):
|
||||
relative_to_base = path.relative_to(base)
|
||||
if str(relative_to_base).startswith("09-templates/"):
|
||||
continue
|
||||
text = path.read_text(encoding="utf-8", errors="replace")
|
||||
lowered = text.lower()
|
||||
@@ -280,18 +280,18 @@ def project_search_memory(args: dict[str, Any]) -> dict[str, Any]:
|
||||
continue
|
||||
start = max(0, index - 220)
|
||||
end = min(len(text), index + len(query) + 220)
|
||||
matches.append({"path": str(path.relative_to(ROOT)), "snippet": text[start:end].strip()})
|
||||
matches.append({"path": rel(path), "snippet": text[start:end].strip()})
|
||||
if len(matches) >= limit:
|
||||
break
|
||||
return tool_result({"profile": profile, "canonical": True, "query": query, "matches": matches})
|
||||
|
||||
|
||||
def index_path(profile: str) -> Path:
|
||||
return INDEX_ROOT / profile / "project-knowledge.jsonl"
|
||||
return aiw_profile.index_dir(profile, root=ROOT) / "project-knowledge.jsonl"
|
||||
|
||||
|
||||
def index_manifest_path(profile: str) -> Path:
|
||||
return INDEX_ROOT / profile / "manifest.json"
|
||||
return aiw_profile.index_dir(profile, root=ROOT) / "manifest.json"
|
||||
|
||||
|
||||
def search_tokens(text: str) -> set[str]:
|
||||
|
||||
@@ -175,7 +175,7 @@ class ContextMCPTests(unittest.TestCase):
|
||||
}) + "\n", encoding="utf-8")
|
||||
manifest.write_text(json.dumps({"chunk_count": 1}), encoding="utf-8")
|
||||
|
||||
with patch.object(server, "ROOT", root), patch.object(server, "INDEX_ROOT", root / ".aiw" / "indexes"):
|
||||
with patch.object(server, "ROOT", root):
|
||||
result = server.memory_hybrid_search({"profile": "fidelity", "query": "dismissal lifecycle"})["structuredContent"]
|
||||
|
||||
self.assertTrue(result["index_available"])
|
||||
@@ -189,13 +189,31 @@ class ContextMCPTests(unittest.TestCase):
|
||||
real.parent.mkdir(parents=True)
|
||||
real.write_text("Important XFlow context", encoding="utf-8")
|
||||
|
||||
with patch.object(server, "ROOT", root), patch.object(server, "INDEX_ROOT", root / ".aiw" / "indexes"):
|
||||
with patch.object(server, "ROOT", root):
|
||||
result = server.memory_hybrid_search({"profile": "fidelity", "query": "XFlow"})["structuredContent"]
|
||||
|
||||
self.assertFalse(result["index_available"])
|
||||
self.assertEqual(result["source"], "live-project-knowledge-fallback")
|
||||
self.assertEqual(len(result["matches"]), 1)
|
||||
|
||||
def test_project_context_uses_workspace_json_paths(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
config = root / "profiles" / "demo" / "workspace.json"
|
||||
current = root / "workspaces" / "demo" / "project-knowledge" / "01-current" / "current-work.md"
|
||||
work_items = root / "workspaces" / "demo" / "project-knowledge" / "01-current" / "work-items.md"
|
||||
config.parent.mkdir(parents=True)
|
||||
current.parent.mkdir(parents=True)
|
||||
config.write_text(json.dumps({"knowledge_dir": "workspaces/demo/project-knowledge"}), encoding="utf-8")
|
||||
current.write_text("# Current\nDemo current work", encoding="utf-8")
|
||||
work_items.write_text("# Work Items", encoding="utf-8")
|
||||
|
||||
with patch.object(server, "ROOT", root):
|
||||
result = server.project_current_context({"profile": "demo"})["structuredContent"]
|
||||
|
||||
self.assertEqual(result["files"][0]["path"], "workspaces/demo/project-knowledge/01-current/current-work.md")
|
||||
self.assertIn("Demo current work", result["files"][0]["text"])
|
||||
|
||||
def test_previous_workday_skips_weekend(self) -> None:
|
||||
monday = date(2026, 5, 18)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user