@mohammadhprp/system-prompt 0.12.4 → 0.12.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/framework/commands/review.md +5 -20
- package/framework/skills/gh/SKILL.md +157 -0
- package/framework/skills/gh/examples.md +10 -0
- package/framework/skills/ponytail/SKILL.md +145 -0
- package/framework/skills/ponytail/references/ponytail-audit.md +18 -0
- package/framework/skills/ponytail/references/ponytail-debt.md +21 -0
- package/framework/skills/ponytail/references/ponytail-gain.md +25 -0
- package/framework/skills/ponytail/references/ponytail-help.md +18 -0
- package/framework/skills/ponytail/references/ponytail-mode.md +33 -0
- package/framework/skills/ponytail/references/ponytail-review.md +27 -0
- package/framework/skills/ponytail/references/ponytail-rules.md +31 -0
- package/framework/skills/ponytail/references/principle-boundary-discipline.md +7 -0
- package/framework/skills/ponytail/references/principle-encode-lessons-in-structure.md +13 -0
- package/framework/skills/ponytail/references/principle-fix-root-causes.md +17 -0
- package/framework/skills/ponytail/references/principle-make-operations-idempotent.md +12 -0
- package/framework/skills/ponytail/references/principle-model-the-domain.md +7 -0
- package/framework/skills/ponytail/references/principle-prove-it-works.md +27 -0
- package/framework/skills/ponytail/references/principle-sequence-verifiable-units.md +7 -0
- package/framework/skills/review/SKILL.md +106 -11
- package/framework/skills/review/examples.md +4 -3
- package/framework/skills/review/scripts/render_review.py +95 -0
- package/framework/skills/review/scripts/resolve_spec_context.py +723 -0
- package/framework/skills/review/scripts/validate_review_json.py +348 -0
- package/package.json +1 -1
- package/src/catalog.js +4 -2
|
@@ -1,18 +1,113 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: review
|
|
3
|
-
description:
|
|
3
|
+
description: Review local Git changes, GitHub pull requests, or GitLab merge requests and produce a structured review.json artifact. Use this skill whenever the user asks for a code review, diff review, PR review, MR review, or production-readiness assessment. Use the gh skill for GitHub and the glab skill for GitLab; never publish review comments.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Review Skill
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Review the requested change and write the result to `review.json`. The review is
|
|
9
|
+
read-only: do not approve, request changes, comment, merge, push, or otherwise
|
|
10
|
+
modify remote repositories.
|
|
9
11
|
|
|
10
|
-
##
|
|
12
|
+
## Select the change source
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
Use the source explicitly named by the user. If it is not named, inspect the
|
|
15
|
+
current repository and ask when the target is ambiguous.
|
|
16
|
+
|
|
17
|
+
### Local changes
|
|
18
|
+
|
|
19
|
+
- Run `git status --short` and inspect staged and unstaged changes with
|
|
20
|
+
`git diff`, `git diff --cached`, and the relevant branch comparison when
|
|
21
|
+
needed.
|
|
22
|
+
- Review the working tree as it exists; do not checkout, reset, stash, or amend
|
|
23
|
+
user changes.
|
|
24
|
+
|
|
25
|
+
### GitHub pull requests
|
|
26
|
+
|
|
27
|
+
- Read the `gh` skill before using GitHub commands.
|
|
28
|
+
- Confirm repository context with `git remote -v` or use `--repo OWNER/REPO`.
|
|
29
|
+
- Read metadata with `gh pr view NUMBER --json title,body,baseRefName,headRefName`.
|
|
30
|
+
- Read the patch with `gh pr diff NUMBER`.
|
|
31
|
+
- Use only read operations. Do not run `gh pr review`, `gh pr comment`, `gh api`
|
|
32
|
+
mutations, or any other publishing command.
|
|
33
|
+
|
|
34
|
+
### GitLab merge requests
|
|
35
|
+
|
|
36
|
+
- Read the `glab` skill before using GitLab commands.
|
|
37
|
+
- Confirm repository context with `git remote -v` or use `-R OWNER/REPO`.
|
|
38
|
+
- Read metadata with `glab mr view NUMBER --output=json`.
|
|
39
|
+
- Read the patch with `glab mr diff NUMBER`.
|
|
40
|
+
- Use only read operations. Do not run `glab mr approve`, `glab mr note`, merge,
|
|
41
|
+
or other mutating commands.
|
|
42
|
+
|
|
43
|
+
## Review process
|
|
44
|
+
|
|
45
|
+
1. Establish the change intent from the request, commit history, description,
|
|
46
|
+
and diff. Focus findings on changed files and lines.
|
|
47
|
+
2. Read relevant repository guidance and contracts, including naming, testing,
|
|
48
|
+
security, and performance standards when available.
|
|
49
|
+
3. Check correctness, edge cases, error handling, state transitions,
|
|
50
|
+
compatibility, security, and meaningful performance risks.
|
|
51
|
+
4. Check maintainability, comments, and tests against the consuming
|
|
52
|
+
repository's conventions. Treat changed comments and tests as review items.
|
|
53
|
+
5. Include only actionable findings. Put concerns about untouched code in the
|
|
54
|
+
top-level body rather than attaching them to changed lines.
|
|
55
|
+
6. Classify findings as critical, important, suggestions, or nits. Do not block
|
|
56
|
+
for speculative improvements or cosmetic preferences.
|
|
57
|
+
|
|
58
|
+
## Inline comments
|
|
59
|
+
|
|
60
|
+
Use inline comments only when the exact changed path, side, and line are
|
|
61
|
+
available in the reviewed diff. For annotated diffs, use `[OLD:n]` as `LEFT`,
|
|
62
|
+
`[NEW:n]` as `RIGHT`, and `[OLD:n,NEW:m]` as `RIGHT` line `m`. If an exact
|
|
63
|
+
location cannot be verified, put the finding in the top-level body.
|
|
64
|
+
|
|
65
|
+
Every inline comment must begin with one of:
|
|
66
|
+
|
|
67
|
+
- `🚨 [CRITICAL]`
|
|
68
|
+
- `⚠️ [IMPORTANT]`
|
|
69
|
+
- `💡 [SUGGESTION]`
|
|
70
|
+
- `🧹 [NIT]` (only with a concrete suggestion)
|
|
71
|
+
|
|
72
|
+
Keep comments concise, actionable, and limited to changed lines. Use a
|
|
73
|
+
`suggestion` block only when the replacement is exact and safe.
|
|
74
|
+
|
|
75
|
+
## Output contract
|
|
76
|
+
|
|
77
|
+
Write exactly one `review.json` with this shape:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"verdict": "APPROVE",
|
|
82
|
+
"body": "## Overview\n...\n\n## Concerns\n...\n\nFound: 0 critical, 0 important, 0 suggestions\n\nApprove",
|
|
83
|
+
"comments": []
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
- `verdict` is required and must be `APPROVE` or `REJECT`.
|
|
88
|
+
- `body` is required and must contain an overview, concerns, issue counts in
|
|
89
|
+
the form `Found: X critical, Y important, Z suggestions`, and a final
|
|
90
|
+
recommendation of `Approve`, `Approve with nits`, or `Request changes` that
|
|
91
|
+
agrees with `verdict`.
|
|
92
|
+
- `comments` is required and must be an array. Use an empty array when no
|
|
93
|
+
verified inline comment is appropriate.
|
|
94
|
+
- Paths are repository-relative. `side` and `start_side` are `LEFT` or
|
|
95
|
+
`RIGHT`; `start_line` is used only for a multi-line range.
|
|
96
|
+
|
|
97
|
+
Before finishing, validate JSON and, when an annotated diff is available, run:
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
python3 .agents/skills/review/scripts/validate_review_json.py \
|
|
101
|
+
--review-json review.json --diff pr_diff.txt
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
If that path is unavailable, run the validator from this skill's `scripts/`
|
|
105
|
+
directory. Then render the review for human inspection:
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
python3 .agents/skills/review/scripts/render_review.py \
|
|
109
|
+
--review-json review.json --output review.html
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The renderer creates a self-contained Excalidraw-inspired dark HTML report.
|
|
113
|
+
The final artifacts are `review.json` and `review.html`.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Examples
|
|
2
2
|
|
|
3
|
-
- Review
|
|
4
|
-
-
|
|
5
|
-
-
|
|
3
|
+
- Review staged and unstaged local changes, then write a structured `review.json`.
|
|
4
|
+
- Review a GitHub pull request with `gh pr view` and `gh pr diff`, or a GitLab merge request with `glab mr view` and `glab mr diff`.
|
|
5
|
+
- Check correctness, tests, performance, security, and maintainability; never publish comments or approvals.
|
|
6
|
+
- Render the completed `review.json` to `review.html` for a visual, dark-mode review summary.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Render a review.json file as a self-contained dark HTML report."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
PAGE = r"""<!doctype html>
|
|
12
|
+
<html lang="en">
|
|
13
|
+
<head>
|
|
14
|
+
<meta charset="utf-8">
|
|
15
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
16
|
+
<title>Code review</title>
|
|
17
|
+
<style>
|
|
18
|
+
:root { color-scheme: dark; --bg:#121212; --panel:#1b1b1b; --ink:#f4f4f0; --muted:#a9aaa5; --line:#3b3b3b; --blue:#8cc8ff; --red:#ff7d7d; --orange:#ffbf69; --green:#78dba9; --purple:#c7a6ff; }
|
|
19
|
+
* { box-sizing:border-box; }
|
|
20
|
+
body { margin:0; background:var(--bg); color:var(--ink); font:16px/1.55 ui-rounded, "Comic Sans MS", system-ui, sans-serif; }
|
|
21
|
+
body:before { content:""; position:fixed; inset:0; pointer-events:none; opacity:.08; background-image:radial-gradient(#fff 0.7px,transparent .7px); background-size:13px 13px; }
|
|
22
|
+
main { width:min(1060px, calc(100% - 32px)); margin:48px auto; }
|
|
23
|
+
.eyebrow { color:var(--blue); font:700 12px/1 monospace; letter-spacing:.16em; text-transform:uppercase; }
|
|
24
|
+
h1 { font-size:clamp(32px,6vw,62px); line-height:1; margin:10px 0 28px; letter-spacing:-.06em; }
|
|
25
|
+
.hero, .panel { background:var(--panel); border:2px solid var(--line); border-radius:13px 10px 15px 9px; box-shadow:5px 5px 0 #080808; }
|
|
26
|
+
.hero { padding:24px; display:flex; align-items:center; justify-content:space-between; gap:20px; }
|
|
27
|
+
.copy-prompt { cursor:pointer; color:#121212; background:var(--blue); border:2px solid var(--blue); border-radius:8px 6px 9px 5px; padding:7px 10px; font:800 11px monospace; text-transform:uppercase; box-shadow:2px 2px 0 #080808; }
|
|
28
|
+
.copy-prompt:hover { transform:translate(-1px,-1px); box-shadow:4px 4px 0 #080808; } .copy-status { color:var(--muted); font-size:12px; }
|
|
29
|
+
.verdict { border:2px solid currentColor; border-radius:10px 8px 11px 7px; padding:11px 16px; font-weight:800; letter-spacing:.08em; }
|
|
30
|
+
.approve { color:var(--green); } .reject { color:var(--red); }
|
|
31
|
+
.stats { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin:22px 0; }
|
|
32
|
+
.stat { padding:16px; border:2px solid var(--line); border-radius:8px 11px 7px 10px; background:#171717; }
|
|
33
|
+
.stat b { display:block; font-size:30px; line-height:1; } .stat span { color:var(--muted); font-size:12px; text-transform:uppercase; letter-spacing:.08em; }
|
|
34
|
+
.panel { padding:22px; margin:22px 0; } h2 { margin:0 0 14px; font-size:22px; }
|
|
35
|
+
.body { white-space:pre-wrap; color:#e4e4df; }
|
|
36
|
+
.filters { display:flex; flex-wrap:wrap; gap:8px; margin:-2px 0 16px; }
|
|
37
|
+
.filters button { cursor:pointer; color:var(--muted); background:#121212; border:1px solid var(--line); border-radius:7px 5px 8px 6px; padding:7px 11px; font:700 12px monospace; text-transform:uppercase; }
|
|
38
|
+
.filters button.active, .filters button:hover { color:var(--ink); border-color:var(--blue); box-shadow:2px 2px 0 #080808; }
|
|
39
|
+
.finding.hidden { display:none; }
|
|
40
|
+
.finding { border-left:5px solid var(--line); padding:14px 16px 16px; margin:12px 0; background:#151515; border-radius:4px 10px 8px 5px; }
|
|
41
|
+
.finding.critical { border-color:var(--red); } .finding.important { border-color:var(--orange); } .finding.suggestion { border-color:var(--blue); } .finding.nit { border-color:var(--purple); }
|
|
42
|
+
.finding-head { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:9px; }
|
|
43
|
+
.location { color:var(--blue); font:700 13px monospace; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } .comment { white-space:pre-wrap; }
|
|
44
|
+
.severity { display:inline-flex; align-items:center; gap:6px; color:var(--muted); font:700 11px monospace; letter-spacing:.08em; text-transform:uppercase; margin-bottom:7px; }
|
|
45
|
+
.severity.critical { color:var(--red); } .severity.important { color:var(--orange); } .severity.suggestion { color:var(--blue); } .severity.nit { color:var(--purple); }
|
|
46
|
+
.codeblock { overflow:auto; margin:10px 0 0; padding:13px; background:#0c0c0c; border:1px solid var(--line); border-radius:7px; color:#d7d7d0; font:13px/1.6 ui-monospace, SFMono-Regular, Consolas, monospace; white-space:pre; }
|
|
47
|
+
.tok-keyword { color:#c7a6ff; } .tok-string { color:#a8d8a8; } .tok-number { color:#ffbf69; } .tok-comment { color:#777b78; font-style:italic; }
|
|
48
|
+
@media (max-width:650px) { main{margin:24px auto}.hero{align-items:flex-start;flex-direction:column}.stats{grid-template-columns:repeat(2,1fr)} }
|
|
49
|
+
</style>
|
|
50
|
+
</head>
|
|
51
|
+
<body><main>
|
|
52
|
+
<div class="eyebrow">review artifact // visualized</div><h1>Code review</h1>
|
|
53
|
+
<section class="hero"><div><div class="eyebrow">verdict</div><div id="verdict"></div></div><div id="summary"></div></section>
|
|
54
|
+
<section class="stats" id="stats"></section>
|
|
55
|
+
<section class="panel"><h2>Review notes</h2><div class="body" id="body"></div></section>
|
|
56
|
+
<section class="panel"><h2>Inline findings</h2><div class="filters" id="filters"><button class="active" data-filter="all">All</button><button data-filter="critical">Critical</button><button data-filter="important">Important</button><button data-filter="suggestion">Suggestions</button><button data-filter="nit">Nits</button></div><div id="comments"></div></section>
|
|
57
|
+
</main><script>
|
|
58
|
+
const review = __REVIEW_DATA__;
|
|
59
|
+
const esc = value => String(value ?? '').replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
|
60
|
+
const comments = Array.isArray(review.comments) ? review.comments : [];
|
|
61
|
+
const counts = { critical:0, important:0, suggestion:0, nit:0 };
|
|
62
|
+
const kind = body => { const text=String(body||'').toLowerCase(); return text.includes('critical')?'critical':text.includes('important')?'important':text.includes('suggestion')?'suggestion':text.includes('nit')?'nit':'suggestion'; };
|
|
63
|
+
const severity = { critical:['🚨','Critical'], important:['⚠️','Important'], suggestion:['💡','Suggestion'], nit:['🧹','Nit'] };
|
|
64
|
+
const cleanBody = body => String(body || '').replace(/^[^\w\n]*\[(?:CRITICAL|IMPORTANT|SUGGESTION|NIT)\]\s*/i, '');
|
|
65
|
+
const highlighted = code => { const pattern=/(\/\/.*|#.*|\/\*[\s\S]*?\*\/|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`|\b(?:const|let|var|function|return|if|else|for|while|class|new|throw|async|await|import|from|true|false|null|undefined)\b|\b\d+(?:\.\d+)?\b)/g; return String(code).split(pattern).map((part, i) => { if (i % 2 === 0) return esc(part); const cls=/^(\/\/|#|\/\*)/.test(part)?'tok-comment':/^["'`]/.test(part)?'tok-string':/^\d/.test(part)?'tok-number':'tok-keyword'; return `<span class="${cls}">${esc(part)}</span>`; }).join(''); };
|
|
66
|
+
const formatBody = body => { const source=String(body||''), pattern=/```(?:[\w+-]+)?\n([\s\S]*?)```/g; let result='', last=0, match; while ((match=pattern.exec(source))) { result += esc(source.slice(last, match.index)); result += `<pre class="codeblock"><code>${highlighted(match[1])}</code></pre>`; last=pattern.lastIndex; } return result + esc(source.slice(last)); };
|
|
67
|
+
comments.forEach(c => counts[kind(c.body)]++);
|
|
68
|
+
const verdict = review.verdict === 'REJECT' ? 'REJECT' : 'APPROVE';
|
|
69
|
+
document.title = `${verdict} · Code review`;
|
|
70
|
+
document.querySelector('#verdict').innerHTML = `<div class="verdict ${verdict === 'REJECT' ? 'reject':'approve'}">${verdict}</div>`;
|
|
71
|
+
document.querySelector('#summary').textContent = `${comments.length} inline ${comments.length === 1 ? 'finding':'findings'}`;
|
|
72
|
+
document.querySelector('#body').textContent = review.body || 'No review body provided.';
|
|
73
|
+
document.querySelector('#stats').innerHTML = Object.entries(counts).map(([name,count]) => `<div class="stat"><b>${count}</b><span>${name}</span></div>`).join('');
|
|
74
|
+
document.querySelector('#comments').innerHTML = comments.length ? comments.map((c, i) => { const type=kind(c.body), [icon,label]=severity[type]; const location = `${esc(c.path || 'unknown file')}:${esc(c.line ?? '?')} · ${esc(c.side || '')}`; return `<article class="finding ${type}" data-kind="${type}"><div class="finding-head"><div><div class="location">${location}</div><div class="severity ${type}">${icon} ${label}</div></div><button class="copy-prompt" type="button" data-finding="${i}">Copy fix prompt</button></div><div class="comment">${formatBody(cleanBody(c.body))}</div><span class="copy-status" aria-live="polite"></span></article>`; }).join('') : '<div class="body">No inline findings.</div>';
|
|
75
|
+
document.querySelector('#filters').addEventListener('click', event => { const button=event.target.closest('button'); if (!button) return; const filter=button.dataset.filter; document.querySelectorAll('#filters button').forEach(item => item.classList.toggle('active', item === button)); document.querySelectorAll('.finding').forEach(item => item.classList.toggle('hidden', filter !== 'all' && item.dataset.kind !== filter)); });
|
|
76
|
+
const fixPrompt = finding => `You are fixing one code review finding. Apply the smallest necessary change in the repository; do not weaken behavior or remove coverage.\n\nReview verdict: ${verdict}\n\nReview context:\n${review.body || 'No review body provided.'}\n\nTarget finding:\n[${String(finding.body || '').split(' ')[1] || 'FINDING'}] ${finding.path || 'unknown file'}:${finding.line ?? '?'} (${finding.side || 'RIGHT'})\n${finding.body || ''}\n\nAfter fixing this finding, run the relevant tests and summarize the change and test result.`;
|
|
77
|
+
const copyText = async (text, status) => { try { await navigator.clipboard.writeText(text); } catch (_) { const area=document.createElement('textarea'); area.value=text; area.style.position='fixed'; area.style.opacity='0'; document.body.append(area); area.select(); document.execCommand('copy'); area.remove(); } status.textContent='Copied'; setTimeout(() => { status.textContent=''; }, 2200); };
|
|
78
|
+
document.querySelector('#comments').addEventListener('click', event => { const button=event.target.closest('[data-finding]'); if (!button) return; const finding=comments[Number(button.dataset.finding)]; copyText(fixPrompt(finding), button.parentElement.parentElement.querySelector('.copy-status')); });
|
|
79
|
+
</script></body></html>"""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def main() -> None:
|
|
83
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
84
|
+
parser.add_argument("--review-json", default="review.json")
|
|
85
|
+
parser.add_argument("--output", default="review.html")
|
|
86
|
+
args = parser.parse_args()
|
|
87
|
+
review = json.loads(Path(args.review_json).read_text(encoding="utf-8"))
|
|
88
|
+
data = json.dumps(review, ensure_ascii=False, separators=(",", ":"))
|
|
89
|
+
data = data.replace("<", "\\u003c").replace(">", "\\u003e").replace("&", "\\u0026")
|
|
90
|
+
html = PAGE.replace("__REVIEW_DATA__", data)
|
|
91
|
+
Path(args.output).write_text(html, encoding="utf-8")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
if __name__ == "__main__":
|
|
95
|
+
main()
|