generated from coulomb/repo-seed
Provide access to all scope files
This commit is contained in:
2
Makefile
2
Makefile
@@ -22,7 +22,7 @@ start: ## Start the API server in the background on port 8002
|
||||
for i in $$(seq 1 50); do \
|
||||
if [ -f $(PIDFILE) ]; then PID=$$(cat $(PIDFILE)); fi; \
|
||||
if [ -n "$$PID" ] && ! kill -0 $$PID 2>/dev/null; then break; fi; \
|
||||
if curl -fsS $(HEALTH_URL) >/dev/null 2>&1; then \
|
||||
if [ -n "$$PID" ] && curl -fsS $(HEALTH_URL) >/dev/null 2>&1; then \
|
||||
echo "Started (PID $$PID) — http://127.0.0.1:$(PORT)/ui"; \
|
||||
exit 0; \
|
||||
fi; \
|
||||
|
||||
@@ -17,12 +17,23 @@ router = APIRouter(include_in_schema=False)
|
||||
APP_NAME = "Repository Scoping"
|
||||
|
||||
|
||||
def page(title: str, body: str, selected_repository: str | None = None) -> HTMLResponse:
|
||||
repository_context = (
|
||||
f'<span class="header-context">{escape(selected_repository)}</span>'
|
||||
if selected_repository
|
||||
else ""
|
||||
)
|
||||
def page(
|
||||
title: str,
|
||||
body: str,
|
||||
selected_repository: str | None = None,
|
||||
selected_repository_id: int | None = None,
|
||||
) -> HTMLResponse:
|
||||
if selected_repository and selected_repository_id is not None:
|
||||
repository_context = (
|
||||
f'<a class="header-context" href="/ui/repos/{selected_repository_id}">'
|
||||
f"{escape(selected_repository)}</a>"
|
||||
)
|
||||
elif selected_repository:
|
||||
repository_context = (
|
||||
f'<span class="header-context">{escape(selected_repository)}</span>'
|
||||
)
|
||||
else:
|
||||
repository_context = ""
|
||||
return HTMLResponse(
|
||||
f"""
|
||||
<!doctype html>
|
||||
@@ -197,7 +208,6 @@ def page(title: str, body: str, selected_repository: str | None = None) -> HTMLR
|
||||
<nav class="actions">
|
||||
<a href="/ui/search">Search</a>
|
||||
<a href="/ui/discovery">Discovery</a>
|
||||
<a href="/ui/scope">SCOPE</a>
|
||||
<a href="/docs">API Docs</a>
|
||||
</nav>
|
||||
</header>
|
||||
@@ -307,6 +317,43 @@ def scope_document() -> HTMLResponse:
|
||||
return page("SCOPE.md", body)
|
||||
|
||||
|
||||
@router.get("/ui/repos/{repository_id}/scope")
|
||||
def repository_scope_document(
|
||||
repository_id: int,
|
||||
service: RegistryService = Depends(get_service),
|
||||
) -> HTMLResponse:
|
||||
repository = service.get_repository(repository_id)
|
||||
checkout = service.ingestion.cached_checkout(repository.url)
|
||||
if checkout is None:
|
||||
rendered = (
|
||||
'<p class="muted">No cached checkout exists for this repository yet. '
|
||||
"Run analysis first, or use a local repository path.</p>"
|
||||
)
|
||||
else:
|
||||
scope_path = checkout.source_path / "SCOPE.md"
|
||||
if scope_path.exists():
|
||||
content = scope_path.read_text(encoding="utf-8")
|
||||
rendered = f'<pre class="scope-document">{escape(content)}</pre>'
|
||||
else:
|
||||
rendered = (
|
||||
'<p class="muted">No SCOPE.md file was found in this '
|
||||
"repository checkout.</p>"
|
||||
)
|
||||
body = f"""
|
||||
<h1>SCOPE.md</h1>
|
||||
<section class="panel stack">
|
||||
<p class="muted">Canonical scope summary for the {escape(repository.name)} repository.</p>
|
||||
{rendered}
|
||||
</section>
|
||||
"""
|
||||
return page(
|
||||
"SCOPE.md",
|
||||
body,
|
||||
selected_repository=repository.name,
|
||||
selected_repository_id=repository.id,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/ui/discovery")
|
||||
def discovery_page(service: RegistryService = Depends(get_service)) -> HTMLResponse:
|
||||
repositories = service.list_repositories()
|
||||
@@ -701,6 +748,7 @@ def repository_detail(
|
||||
<div class="actions">
|
||||
<h1 style="margin-right:auto">{escape(repository.name)}</h1>
|
||||
<a class="button secondary" href="/ui/repos/{repository_id}/export">Export</a>
|
||||
<a class="button secondary" href="/ui/repos/{repository_id}/scope">SCOPE</a>
|
||||
<a class="button secondary" href="/ui">Back</a>
|
||||
</div>
|
||||
<p class="muted">{escape(repository.description or '')}</p>
|
||||
@@ -840,7 +888,12 @@ def repository_detail(
|
||||
</form>
|
||||
</section>
|
||||
"""
|
||||
return page(repository.name, body, selected_repository=repository.name)
|
||||
return page(
|
||||
repository.name,
|
||||
body,
|
||||
selected_repository=repository.name,
|
||||
selected_repository_id=repository.id,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/ui/repos/{repository_id}/export")
|
||||
@@ -1296,6 +1349,7 @@ def analysis_run_detail(
|
||||
f"{repository.name} Run {analysis_run_id}",
|
||||
body,
|
||||
selected_repository=repository.name,
|
||||
selected_repository_id=repository.id,
|
||||
)
|
||||
|
||||
|
||||
@@ -1462,7 +1516,12 @@ def repository_element_listing(
|
||||
</table>
|
||||
</section>
|
||||
"""
|
||||
return page(title, body, selected_repository=repository.name)
|
||||
return page(
|
||||
title,
|
||||
body,
|
||||
selected_repository=repository.name,
|
||||
selected_repository_id=repository.id,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/ui/repos/{repository_id}/analysis-runs/{base_analysis_run_id}/diff/{target_analysis_run_id}")
|
||||
@@ -1519,6 +1578,7 @@ def analysis_run_diff_detail(
|
||||
f"{diff.repository.name} Change Review",
|
||||
body,
|
||||
selected_repository=diff.repository.name,
|
||||
selected_repository_id=diff.repository.id,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1209,6 +1209,10 @@ def test_ui_register_analyze_and_approve_loop(tmp_path):
|
||||
"# UI Repo\nReports service status through API and CLI entry points.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(source / "SCOPE.md").write_text(
|
||||
"# SCOPE\n\n## One-liner\n\nUI Repo owns the status reporting scope.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(source / "requirements.txt").write_text("fastapi\n", encoding="utf-8")
|
||||
(source / "app.py").write_text(
|
||||
"from fastapi import FastAPI\n"
|
||||
@@ -1243,6 +1247,7 @@ def test_ui_register_analyze_and_approve_loop(tmp_path):
|
||||
assert "Explore after registration" in index_response.text
|
||||
assert "Use LLM assistance if configured" in index_response.text
|
||||
assert "Trusted auto-populate after analysis" in index_response.text
|
||||
assert '<a href="/ui/scope">SCOPE</a>' not in index_response.text
|
||||
|
||||
create_response = client.post(
|
||||
"/ui/repos",
|
||||
@@ -1268,6 +1273,19 @@ def test_ui_register_analyze_and_approve_loop(tmp_path):
|
||||
assert "Use LLM assistance if configured" in detail_response.text
|
||||
assert "Trusted auto-populate after analysis" in detail_response.text
|
||||
assert "Repository Metadata" in detail_response.text
|
||||
assert (
|
||||
f'<a class="button secondary" href="/ui/repos/{repository_id}/scope">SCOPE</a>'
|
||||
in detail_response.text
|
||||
)
|
||||
|
||||
repo_scope_response = client.get(f"/ui/repos/{repository_id}/scope")
|
||||
assert repo_scope_response.status_code == 200
|
||||
assert (
|
||||
f'<a class="header-context" href="/ui/repos/{repository_id}">UI Repo</a>'
|
||||
in repo_scope_response.text
|
||||
)
|
||||
assert "Canonical scope summary for the UI Repo repository." in repo_scope_response.text
|
||||
assert "UI Repo owns the status reporting scope." in repo_scope_response.text
|
||||
|
||||
edit_repository_response = client.post(
|
||||
f"{repository_path}/edit",
|
||||
@@ -1305,7 +1323,7 @@ def test_ui_register_analyze_and_approve_loop(tmp_path):
|
||||
assert "1 abilities" in run_detail.text
|
||||
assert "2 capabilities" in run_detail.text
|
||||
assert "2 features" in run_detail.text
|
||||
assert "7 facts" in run_detail.text
|
||||
assert "8 facts" in run_detail.text
|
||||
assert "Content Chunks" in run_detail.text
|
||||
assert "README.md:1-2" in run_detail.text
|
||||
assert "ID " in run_detail.text
|
||||
@@ -1359,7 +1377,10 @@ def test_ui_register_analyze_and_approve_loop(tmp_path):
|
||||
|
||||
approved_detail = client.get(approve_response.headers["location"])
|
||||
assert approved_detail.status_code == 200
|
||||
assert '<span class="header-context">UI Repo Edited</span>' in approved_detail.text
|
||||
assert (
|
||||
f'<a class="header-context" href="/ui/repos/{repository_id}">UI Repo Edited</a>'
|
||||
in approved_detail.text
|
||||
)
|
||||
assert "Approved Characteristics" in approved_detail.text
|
||||
assert "Approved Characteristic Tree" in approved_detail.text
|
||||
assert '<strong><a href="/ui/repos/1">UI Repo</a></strong>' in approved_detail.text
|
||||
@@ -1374,7 +1395,7 @@ def test_ui_register_analyze_and_approve_loop(tmp_path):
|
||||
assert "1 candidate abilities" in approved_detail.text
|
||||
assert "2 candidate capabilities" in approved_detail.text
|
||||
assert "2 candidate features" in approved_detail.text
|
||||
assert "7 candidate facts" in approved_detail.text
|
||||
assert "8 candidate facts" in approved_detail.text
|
||||
assert "Use Approved Registry" in approved_detail.text
|
||||
assert "Search Profile" in approved_detail.text
|
||||
assert "Discovery" in approved_detail.text
|
||||
@@ -1387,7 +1408,10 @@ def test_ui_register_analyze_and_approve_loop(tmp_path):
|
||||
params={"scope": "all", "type": "scopes", "entry_filter": "approved"},
|
||||
)
|
||||
assert scope_listing.status_code == 200
|
||||
assert '<span class="header-context">UI Repo Edited</span>' in scope_listing.text
|
||||
assert (
|
||||
f'<a class="header-context" href="/ui/repos/{repository_id}">UI Repo Edited</a>'
|
||||
in scope_listing.text
|
||||
)
|
||||
assert f'<a href="/ui/repos/{repository_id}">UI Repo</a>' in scope_listing.text
|
||||
assert (
|
||||
f"/ui/repos/{repository_id}/elements?scope=all&entry_filter=approved&type=abilities"
|
||||
@@ -1521,7 +1545,7 @@ def test_ui_register_analyze_and_approve_loop(tmp_path):
|
||||
},
|
||||
)
|
||||
assert filtered_fact_listing.status_code == 200
|
||||
assert "1 of 7 shown" in filtered_fact_listing.text
|
||||
assert "1 of 8 shown" in filtered_fact_listing.text
|
||||
assert "FastAPI" in filtered_fact_listing.text
|
||||
assert "python route decorator" not in filtered_fact_listing.text
|
||||
|
||||
|
||||
Reference in New Issue
Block a user