mirror of
https://github.com/MaksTinyWorkshop/_Assistant_Lead_Tech
synced 2026-04-06 21:41:42 +02:00
mcp: consolidate lot 1 metadata and planning
This commit is contained in:
@@ -10,9 +10,10 @@ from leadtech_bmad_mcp.knowledge import (
|
||||
search_knowledge,
|
||||
search_global_docs,
|
||||
read_knowledge_doc,
|
||||
read_knowledge_document,
|
||||
_extract_excerpt,
|
||||
parse_front_matter,
|
||||
LeadtechPaths,
|
||||
get_paths,
|
||||
)
|
||||
|
||||
|
||||
@@ -122,6 +123,30 @@ def test_search_knowledge_single_bucket(tmp_path):
|
||||
assert all(r["bucket"] == "risques" for r in results)
|
||||
|
||||
|
||||
def test_search_knowledge_uses_front_matter_tags(tmp_path):
|
||||
knowledge = tmp_path / "knowledge"
|
||||
(knowledge / "backend" / "patterns").mkdir(parents=True)
|
||||
(knowledge / "backend" / "patterns" / "nestjs.md").write_text(
|
||||
"---\n"
|
||||
"title: Backend — Patterns : NestJS\n"
|
||||
"tags: [guards, auth]\n"
|
||||
"---\n\n"
|
||||
"# NestJS\n\n"
|
||||
"Texte volontairement sans le mot cle demande.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
paths = LeadtechPaths(
|
||||
root=tmp_path,
|
||||
knowledge=knowledge,
|
||||
capitalisation=tmp_path / "95_a_capitaliser.md",
|
||||
projects_conf=tmp_path / "_projects.conf",
|
||||
)
|
||||
with patch("leadtech_bmad_mcp.knowledge.get_paths", return_value=paths):
|
||||
results = search_knowledge("backend", "guards")
|
||||
assert results
|
||||
assert results[0]["title"] == "Backend — Patterns : NestJS"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# read_knowledge_doc
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -147,6 +172,23 @@ def test_read_knowledge_doc_traversal_blocked(tmp_path):
|
||||
read_knowledge_doc("backend", "patterns", "../../etc/passwd")
|
||||
|
||||
|
||||
def test_read_knowledge_document_splits_metadata_and_body(tmp_path):
|
||||
file_path = tmp_path / "doc.md"
|
||||
file_path.write_text(
|
||||
"---\n"
|
||||
"title: Test Doc\n"
|
||||
"tags: [alpha, beta]\n"
|
||||
"---\n\n"
|
||||
"# Heading\n\n"
|
||||
"Contenu principal.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
doc = read_knowledge_document(file_path)
|
||||
assert doc.metadata["title"] == "Test Doc"
|
||||
assert doc.metadata["tags"] == ["alpha", "beta"]
|
||||
assert doc.body.startswith("# Heading")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# _extract_excerpt
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -176,6 +218,31 @@ def test_extract_excerpt_length_bounded():
|
||||
assert len(excerpt) <= 500
|
||||
|
||||
|
||||
def test_parse_front_matter_returns_body_unchanged_without_header():
|
||||
content = "# Heading\n\nPlain body"
|
||||
metadata, body = parse_front_matter(content)
|
||||
assert metadata == {}
|
||||
assert body == content
|
||||
|
||||
|
||||
def test_parse_front_matter_parses_lists_and_scalars():
|
||||
content = (
|
||||
"---\n"
|
||||
"title: Demo\n"
|
||||
"tags: [alpha, beta]\n"
|
||||
"severity: high\n"
|
||||
"enabled: true\n"
|
||||
"---\n\n"
|
||||
"Body\n"
|
||||
)
|
||||
metadata, body = parse_front_matter(content)
|
||||
assert metadata["title"] == "Demo"
|
||||
assert metadata["tags"] == ["alpha", "beta"]
|
||||
assert metadata["severity"] == "high"
|
||||
assert metadata["enabled"] is True
|
||||
assert body == "Body\n"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# search_global_docs
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -32,7 +32,7 @@ def _mock_mcp_module():
|
||||
|
||||
_mock_mcp_module()
|
||||
|
||||
from leadtech_bmad_mcp.server import validate_plan, validate_patch # noqa: E402
|
||||
from leadtech_bmad_mcp.server import get_guidance, validate_plan, validate_patch # noqa: E402
|
||||
from leadtech_bmad_mcp.knowledge import LeadtechPaths # noqa: E402
|
||||
|
||||
|
||||
@@ -88,6 +88,41 @@ class TestValidatePlanContracts:
|
||||
assert not contract_blocks
|
||||
|
||||
|
||||
class TestGetGuidanceMetadata:
|
||||
def test_exposes_matched_docs_with_metadata(self, tmp_path):
|
||||
paths = _fake_paths(tmp_path)
|
||||
with patch(
|
||||
"leadtech_bmad_mcp.server.search_knowledge",
|
||||
return_value=[
|
||||
{
|
||||
"path": str(tmp_path / "knowledge" / "backend" / "patterns" / "nestjs.md"),
|
||||
"bucket": "patterns",
|
||||
"title": "Backend — Patterns : NestJS",
|
||||
"score": "9",
|
||||
"excerpt": "Pattern sur les guards globaux.",
|
||||
"severity": "medium",
|
||||
"applies_to": "analysis, implementation, review",
|
||||
"tags": "nestjs, guards, auth",
|
||||
}
|
||||
],
|
||||
), patch(
|
||||
"leadtech_bmad_mcp.server.search_global_docs",
|
||||
return_value=[],
|
||||
), patch(
|
||||
"leadtech_bmad_mcp.server.get_paths",
|
||||
return_value=paths,
|
||||
):
|
||||
result = get_guidance("backend", "implementation", story_text="Ajouter un guard NestJS")
|
||||
|
||||
assert result["matched_docs"]
|
||||
matched = result["matched_docs"][0]
|
||||
assert matched["bucket"] == "patterns"
|
||||
assert matched["severity"] == "medium"
|
||||
assert "implementation" in matched["applies_to"]
|
||||
assert matched["read_uri"] == "leadtech://knowledge/backend/patterns/nestjs"
|
||||
assert any("severity=medium" in item for item in result["must_do"])
|
||||
|
||||
|
||||
class TestValidatePlanRequestId:
|
||||
def test_suggests_requestid_when_error_without_requestid(self, tmp_path):
|
||||
paths = _fake_paths(tmp_path)
|
||||
|
||||
Reference in New Issue
Block a user