"""Canonical workflow prompt library for the internal web UI (#428).""" from __future__ import annotations import hashlib import os import re from dataclasses import dataclass from pathlib import Path from typing import Any _WORKFLOW_ROOT = Path("skills/llm-project-workflow/workflows") _DEFAULT_PROMPT_RE = re.compile( r"\*\*Default task prompt:\*\*\s*\n+>\s*(.+?)(?=\n\n|\nDo not improvise)", re.DOTALL, ) _FRONTMATTER_TASK_MODE_RE = re.compile(r"^task_mode:\s*(\S+)", re.MULTILINE) @dataclass(frozen=True) class PromptEntry: slug: str label: str prompt_text: str workflow_path: str task_mode: str | None workflow_hash: str | None source_note: str def _repo_root() -> Path: override = (os.environ.get("WEBUI_REPO_ROOT") or "").strip() if override: return Path(override).resolve() return Path(__file__).resolve().parent.parent def _workflow_file(path: str) -> Path: return _repo_root() / path def _sha256_hex(content: str) -> str: return hashlib.sha256(content.encode("utf-8")).hexdigest() def _read_workflow(path: str) -> tuple[str, str]: file_path = _workflow_file(path) text = file_path.read_text(encoding="utf-8") return text, _sha256_hex(text) def _extract_default_prompt(markdown: str) -> str | None: match = _DEFAULT_PROMPT_RE.search(markdown) if not match: return None lines = [line.strip() for line in match.group(1).splitlines()] return " ".join(line for line in lines if line) def _extract_task_mode(markdown: str) -> str | None: match = _FRONTMATTER_TASK_MODE_RE.search(markdown) return match.group(1) if match else None def _entry_from_workflow( *, slug: str, label: str, workflow_path: str, prompt_override: str | None = None, source_note: str = "", ) -> PromptEntry: markdown, digest = _read_workflow(workflow_path) prompt_text = prompt_override or _extract_default_prompt(markdown) if not prompt_text: raise ValueError(f"No default task prompt found in {workflow_path}") return PromptEntry( slug=slug, label=label, prompt_text=prompt_text, workflow_path=workflow_path, task_mode=_extract_task_mode(markdown), workflow_hash=digest, source_note=source_note, ) def _static_entry( *, slug: str, label: str, prompt_text: str, workflow_path: str, source_note: str, ) -> PromptEntry: path = _workflow_file(workflow_path) digest = _sha256_hex(path.read_text(encoding="utf-8")) if path.is_file() else None markdown = path.read_text(encoding="utf-8") if path.is_file() else "" return PromptEntry( slug=slug, label=label, prompt_text=prompt_text, workflow_path=workflow_path, task_mode=_extract_task_mode(markdown) if markdown else None, workflow_hash=digest, source_note=source_note, ) def load_prompt_library() -> tuple[PromptEntry, ...]: """Load operator prompts derived from canonical workflows.""" entries = ( _entry_from_workflow( slug="review-pr", label="Review PR", workflow_path=str(_WORKFLOW_ROOT / "review-merge-pr.md"), ), _entry_from_workflow( slug="work-issue", label="Work issue", workflow_path=str(_WORKFLOW_ROOT / "work-issue.md"), ), _entry_from_workflow( slug="create-issue", label="Create issue", workflow_path=str(_WORKFLOW_ROOT / "create-issue.md"), ), _static_entry( slug="comment-issue", label="Comment on issue", workflow_path=str(_WORKFLOW_ROOT / "create-issue.md"), prompt_text=( "Comment on the target Gitea issue only if exact comment_issue " "capability is proven. Load the canonical create-issue workflow " "first and follow §16 (comment-on-existing issue rule). Include " "specific evidence; do not duplicate existing comments." ), source_note="Derived from create-issue.md §16; full policy remains in the workflow file.", ), _static_entry( slug="cleanup", label="Post-merge cleanup", workflow_path="skills/llm-project-workflow/templates/worktree-cleanup.md", prompt_text=( "Task: clean up branch/worktree for PR # / issue # after merge. " "Confirm the merge on remote master before any deletion; never " "force-remove a dirty worktree." ), source_note="Full cleanup steps live in templates/worktree-cleanup.md.", ), _entry_from_workflow( slug="audit", label="Reconciliation audit", workflow_path=str(_WORKFLOW_ROOT / "reconcile-landed-pr.md"), ), _static_entry( slug="onboarding", label="Project onboarding", workflow_path="skills/llm-project-workflow/SKILL.md", prompt_text=( "Onboard this repository into the MCP Control Plane: prove identity " "and task capability, configure author/reviewer/reconciler profiles " "in separate namespaces, then complete the checklist at /projects. " "Canonical router: skills/llm-project-workflow/SKILL.md." ), source_note="Checklist details live in webui/data/projects.registry.json and /projects.", ), ) return entries def find_prompt(slug: str) -> PromptEntry | None: for entry in load_prompt_library(): if entry.slug == slug: return entry return None def prompt_to_dict(entry: PromptEntry) -> dict[str, Any]: return { "slug": entry.slug, "label": entry.label, "prompt_text": entry.prompt_text, "workflow_path": entry.workflow_path, "task_mode": entry.task_mode, "workflow_hash": entry.workflow_hash, "source_note": entry.source_note, } def library_to_dict() -> dict[str, Any]: entries = load_prompt_library() return { "count": len(entries), "prompts": [prompt_to_dict(entry) for entry in entries], }