File size: 4,543 Bytes
7d4338a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import json
from pathlib import Path

_DIR = Path(__file__).parent.parent
_CFG = None
_TMPL = None
_CHECKLIST_GUIDANCE = None


def _load_config() -> dict:
    global _CFG
    if _CFG is not None:
        return _CFG

    path = _DIR / "webui" / "plugin-validator-checks.json"
    try:
        _CFG = json.loads(path.read_text())
        return _CFG
    except Exception as e:
        raise RuntimeError(f"Unable to load plugin validator checks: {e}") from e


def _load_template() -> str:
    global _TMPL
    if _TMPL is not None:
        return _TMPL

    path = _DIR / "webui" / "plugin-validator-prompt.md"
    try:
        _TMPL = path.read_text()
        return _TMPL
    except Exception as e:
        raise RuntimeError(f"Unable to load plugin validator prompt template: {e}") from e


def _load_guidance() -> str:
    global _CHECKLIST_GUIDANCE
    if _CHECKLIST_GUIDANCE is not None:
        return _CHECKLIST_GUIDANCE

    path = _DIR / "webui" / "plugin-validator-guidance.md"
    try:
        _CHECKLIST_GUIDANCE = path.read_text().strip()
        return _CHECKLIST_GUIDANCE
    except Exception as e:
        raise RuntimeError(f"Unable to load plugin validator guidance: {e}") from e


def _sanitize_target(value: str) -> str:
    return (value or "").strip().replace("{", "(").replace("}", ")")


def _target_reference(source_type: str, target: str) -> str:
    target = _sanitize_target(target)
    if source_type == "local" and target and "/" not in target and "\\" not in target:
        return f"usr/plugins/{target}/"
    return target


def _source_label(source_type: str) -> str:
    return {
        "local": "Local Plugin",
        "git": "Git Repository",
        "zip": "Uploaded ZIP",
    }.get(source_type, "Plugin Source")


def _source_instructions(source_type: str, target: str, cleanup_target: str | None = None) -> str:
    target_ref = _target_reference(source_type, target)
    cleanup_ref = _sanitize_target(cleanup_target or target_ref)

    if source_type == "git":
        return (
            f"Clone `{target_ref}` to a temporary directory outside the workspace, such as "
            "`/tmp/plugin-validate-$(date +%s)`. Validate the cloned files there. After the review, "
            "run `rm -rf /tmp/plugin-validate-*` and verify cleanup with `ls /tmp/plugin-validate-* 2>&1`."
        )

    if source_type == "zip":
        return (
            f"The ZIP has already been extracted to `{target_ref}`. Validate the plugin from that extracted "
            "directory only. Do not install or move it. After the review, delete that extracted directory "
            f"with `rm -rf \"{cleanup_ref}\"` and verify cleanup with `ls \"{cleanup_ref}\" 2>&1`."
        )

    return (
        f"Read the plugin directly from `{target_ref}`. Do not clone, move, or modify the plugin. "
        "No temporary cleanup is required for this source."
    )


def build_prompt(
    source_type: str,
    target: str,
    checks: list | None = None,
    cleanup_target: str | None = None,
) -> str:
    cfg = _load_config()
    ratings, all_checks = cfg["ratings"], cfg["checks"]
    keys = list(all_checks.keys()) if checks is None else [k for k in checks if k in all_checks]
    prompt_template = _load_template()

    subs = {
        "SOURCE_LABEL": _source_label(source_type),
        "TARGET_REFERENCE": _target_reference(source_type, target),
        "SOURCE_INSTRUCTIONS": _source_instructions(source_type, target, cleanup_target),
        "SELECTED_CHECKS": (
            "\n".join(f"- **{all_checks[k]['label']}**" for k in keys)
            if keys
            else "- (no validation phases selected)"
        ),
        "CHECK_DETAILS": (
            "\n\n".join(
                f"#### {c['label']}\n{c['detail']}\n\nCriteria:\n"
                + "\n".join(f"  - {ratings[level]['icon']} {desc}" for level, desc in c["criteria"].items())
                for c in (all_checks[k] for k in keys)
            )
            if keys
            else "(no validation phases selected)"
        ),
        "CHECKLIST_GUIDANCE": _load_guidance(),
        "STATUS_LEGEND": "\n".join(f"- {r['icon']} **{r['label']}**" for r in ratings.values()),
        "RATING_ICONS": "/".join(r["icon"] for r in ratings.values()),
        "RATING_PASS": ratings["pass"]["icon"],
        "RATING_WARNING": ratings["warning"]["icon"],
        "RATING_FAIL": ratings["fail"]["icon"],
    }
    prompt = prompt_template
    for key, val in subs.items():
        prompt = prompt.replace(f"{{{{{key}}}}}", val)
    return prompt