mirror of
https://github.com/wilsonfreitas/awesome-quant.git
synced 2026-07-27 20:07:50 +00:00
Add Codex workflow support
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: bprr
|
||||
description: Bulk PR reviewer for awesome-quant. Use when the user asks to review all open PRs, review unreviewed PRs, bulk review, or mentions "bprr". Reviews open PRs lacking the reviewed label and presents a summary before any merge/comment/label action.
|
||||
---
|
||||
|
||||
# BPRR: Bulk PR Reviewer
|
||||
|
||||
Review multiple open pull requests for README entry contributions.
|
||||
|
||||
## Hard Rules
|
||||
|
||||
1. Use GitHub MCP tools for PR operations. Do not use `gh` for PR review, comments, labels, closing, or merging.
|
||||
2. Filter out PRs with the `reviewed` label unless the user asks to re-review them.
|
||||
3. Do not comment, label, close, or merge until the user explicitly approves each action.
|
||||
4. Do not auto-merge all approved PRs unless the user explicitly selects that option.
|
||||
5. If GitHub MCP tools are unavailable, report the blocker and point to `docs/codex-setup.md`.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. List open PRs sorted oldest first.
|
||||
2. Filter to unreviewed PRs by default.
|
||||
3. Fetch the default-branch `README.md` once for duplicate checks.
|
||||
4. For each PR, fetch details, labels, files, body, and diff.
|
||||
5. Review only added `README.md` entries unless the PR changes other files; flag other file changes as unusual.
|
||||
6. Validate each PR with the same checks as `$sprr`. Use `scripts/validate_readme.py --diff-from <base-ref>` when the PR branch is available locally; otherwise apply the validator's rules manually from the MCP diff.
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
For each added entry, check:
|
||||
|
||||
- Parser regex match.
|
||||
- Required language tags for new non-commercial entries.
|
||||
- Description period before optional `[GitHub](...)`.
|
||||
- `https://` URLs.
|
||||
- Exact optional `[GitHub](https://github.com/owner/repo)` format.
|
||||
- Correct category section.
|
||||
- Commercial placement under `Commercial & Proprietary Services`.
|
||||
- Duplicate project names or URLs.
|
||||
- Activity, archived status, and basic documentation for GitHub repos.
|
||||
- Clear rationale for multiple related projects in one PR.
|
||||
|
||||
## Summary Output
|
||||
|
||||
Present a table:
|
||||
|
||||
```text
|
||||
| PR | Title | Author | Entries | Format | URL | Section | Duplicate | Verdict |
|
||||
|----|-------|--------|---------|--------|-----|---------|-----------|---------|
|
||||
```
|
||||
|
||||
After the table, list any PRs that need detailed notes.
|
||||
|
||||
Ask what to do next. Accept selections like:
|
||||
|
||||
- `merge 123, 124`
|
||||
- `comment 125`
|
||||
- `close 126`
|
||||
- `all approved`
|
||||
- `none`
|
||||
|
||||
Confirm each merge before executing it.
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
name: sprr
|
||||
description: Single PR reviewer for awesome-quant. Use when the user asks to review, validate, comment on, label, close, or merge one specific pull request that adds README.md entries. Triggers include "sprr", "review PR", "check PR", and "validate contribution".
|
||||
---
|
||||
|
||||
# SPRR: Single PR Reviewer
|
||||
|
||||
Review one pull request that adds entries to `README.md`.
|
||||
|
||||
## Hard Rules
|
||||
|
||||
1. Review only the PR number the user requested. If no PR number is given, ask for one.
|
||||
2. Use GitHub MCP tools for PR operations. Do not use `gh` for PR review, comments, labels, closing, or merging.
|
||||
3. Do not modify the PR until the user explicitly approves that action.
|
||||
4. Always present findings before asking whether to comment, label, close, or merge.
|
||||
5. Always ask before merging.
|
||||
6. Enforce `CONTRIBUTING.md` and `AGENTS.md` strictly for new entries.
|
||||
|
||||
If GitHub MCP tools are unavailable, report that PR operations are blocked and point the user to `docs/codex-setup.md`.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Fetch PR details, files changed, labels, comments, and diff with GitHub MCP.
|
||||
2. Confirm whether the `reviewed` label or prior maintainer comments exist, then proceed with full validation anyway.
|
||||
3. Focus on added lines in `README.md`. Flag any other changed files as unusual for a normal contribution.
|
||||
4. Save or reconstruct the PR diff locally only if needed, then validate added README entries with:
|
||||
|
||||
```bash
|
||||
uv run python scripts/validate_readme.py --diff-from <base-ref>
|
||||
```
|
||||
|
||||
If a base ref is not locally available, apply the same checks manually from the fetched diff.
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
For every added entry:
|
||||
|
||||
- It matches `^\s*- \[(.*)\]\((.*)\) - (.*)$`.
|
||||
- New non-commercial entries include backtick language tags followed by ` - `.
|
||||
- Description ends with a period before optional `[GitHub](...)`.
|
||||
- URLs use `https://`.
|
||||
- Optional GitHub link uses `[GitHub](https://github.com/owner/repo)`.
|
||||
- Section placement matches the project's purpose.
|
||||
- Commercial/proprietary projects are under `Commercial & Proprietary Services`.
|
||||
- Project name and URLs are not duplicates of existing README entries.
|
||||
- GitHub projects are active, not archived, and documented.
|
||||
- Multiple projects in one PR are closely related and explained in the PR body.
|
||||
|
||||
## Verdicts
|
||||
|
||||
Use these verdicts:
|
||||
|
||||
- `APPROVE`: entry is ready to merge.
|
||||
- `NEEDS CHANGES`: fixable format, section, URL, description, or documentation issue.
|
||||
- `REJECT`: duplicate, unrelated multi-project PR, empty PR description, archived/abandoned project, or other hard rejection.
|
||||
|
||||
## Output Shape
|
||||
|
||||
Report:
|
||||
|
||||
```text
|
||||
PR #<number>: <title>
|
||||
Author: <author>
|
||||
Prior review: YES/NO
|
||||
Files changed: <files>
|
||||
Entries reviewed: <count>
|
||||
|
||||
Findings:
|
||||
- <entry>: <status and reason>
|
||||
|
||||
Verdict: APPROVE | NEEDS CHANGES | REJECT
|
||||
Recommended action: <merge/comment/close/no action>
|
||||
```
|
||||
|
||||
Then ask for explicit approval before doing the recommended action.
|
||||
|
||||
## Approved Actions After User Consent
|
||||
|
||||
- If approved and user says to merge: merge with squash unless user requests otherwise.
|
||||
- If needs changes and user says to comment: leave one concise comment with all requested fixes, then add `reviewed` if available.
|
||||
- If rejected and user says to close: leave a polite rejection comment and close the PR.
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: update-pypi-dates
|
||||
description: Refresh tracked PyPI last-updated dates in awesome-quant README.md. Use when the user asks to update PyPI dates, refresh PyPI metadata, or run update-pypi-dates.
|
||||
---
|
||||
|
||||
# Update PyPI Dates
|
||||
|
||||
Refresh last release dates for selected PyPI packages listed in `README.md`.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Run the checker:
|
||||
|
||||
```bash
|
||||
uv run python .agents/skills/update-pypi-dates/scripts/check_pypi_dates.py
|
||||
```
|
||||
|
||||
2. Show the user which packages changed.
|
||||
3. Run README validation:
|
||||
|
||||
```bash
|
||||
uv run python scripts/validate_readme.py
|
||||
```
|
||||
|
||||
4. If the user asked for a commit, stage only the relevant files and commit.
|
||||
5. Do not push unless the user explicitly asks.
|
||||
|
||||
## Tracked Packages
|
||||
|
||||
The script currently tracks:
|
||||
|
||||
- `qfrm`
|
||||
- `chinesestockapi`
|
||||
- `tushare`
|
||||
- `metatrader5` / `MetaTrader5`
|
||||
|
||||
To add packages, edit `PYPI_PROJECTS` in `scripts/check_pypi_dates.py`.
|
||||
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check tracked PyPI projects and update README.md last-updated notes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from urllib.request import urlopen
|
||||
|
||||
PYPI_PROJECTS = {
|
||||
"qfrm": "qfrm",
|
||||
"chinesestockapi": "chinesestockapi",
|
||||
"tushare": "tushare",
|
||||
"metatrader5": "MetaTrader5",
|
||||
}
|
||||
|
||||
README_PATH = Path(__file__).resolve().parents[4] / "README.md"
|
||||
|
||||
|
||||
def get_pypi_last_updated(package_name: str) -> str | None:
|
||||
url = f"https://pypi.org/pypi/{package_name}/json"
|
||||
try:
|
||||
with urlopen(url, timeout=10) as response:
|
||||
data = json.loads(response.read().decode("utf-8"))
|
||||
except Exception as exc:
|
||||
print(f"ERROR fetching {package_name}: {exc}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
releases = data.get("releases", {})
|
||||
for version in sorted(releases.keys(), reverse=True):
|
||||
files = releases[version]
|
||||
if not files:
|
||||
continue
|
||||
upload_time = files[0].get("upload_time_iso_8601")
|
||||
if upload_time:
|
||||
return upload_time.split("T", 1)[0]
|
||||
return None
|
||||
|
||||
|
||||
def find_readme_entry(readme_content: str, display_name: str) -> tuple[int | None, str | None]:
|
||||
pattern = rf"- \[{re.escape(display_name)}\]\(https://pypi\.org/project/[\w.-]+/\)[^\n]*"
|
||||
for line_num, line in enumerate(readme_content.splitlines(), 1):
|
||||
if re.search(pattern, line):
|
||||
return line_num, line
|
||||
return None, None
|
||||
|
||||
|
||||
def update_readme_entry(readme_content: str, display_name: str, new_date: str) -> tuple[str, bool]:
|
||||
lines = readme_content.splitlines()
|
||||
line_num, entry = find_readme_entry(readme_content, display_name)
|
||||
if line_num is None or entry is None:
|
||||
print(f"WARN {display_name}: README entry not found")
|
||||
return readme_content, False
|
||||
|
||||
old_line = lines[line_num - 1]
|
||||
new_date_text = f"(Last updated: {new_date})"
|
||||
existing_date_pattern = r"\(Last updated: \d{4}-\d{2}-\d{2}\)"
|
||||
|
||||
if re.search(existing_date_pattern, old_line):
|
||||
new_line = re.sub(existing_date_pattern, new_date_text, old_line)
|
||||
elif old_line.endswith("."):
|
||||
new_line = old_line[:-1] + f" {new_date_text}."
|
||||
else:
|
||||
new_line = old_line + f" {new_date_text}"
|
||||
|
||||
if new_line == old_line:
|
||||
return readme_content, False
|
||||
|
||||
lines[line_num - 1] = new_line
|
||||
trailing_newline = "\n" if readme_content.endswith("\n") else ""
|
||||
return "\n".join(lines) + trailing_newline, True
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if not README_PATH.exists():
|
||||
print(f"ERROR README.md not found at {README_PATH}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
readme_content = README_PATH.read_text(encoding="utf-8")
|
||||
updated_content = readme_content
|
||||
changes: list[tuple[str, str]] = []
|
||||
|
||||
print("Checking PyPI projects for last-updated dates...")
|
||||
for display_name, package_name in PYPI_PROJECTS.items():
|
||||
last_updated = get_pypi_last_updated(package_name)
|
||||
if last_updated is None:
|
||||
print(f"{display_name}: skipped")
|
||||
continue
|
||||
|
||||
updated_content, changed = update_readme_entry(updated_content, display_name, last_updated)
|
||||
if changed:
|
||||
changes.append((display_name, last_updated))
|
||||
print(f"{display_name}: updated to {last_updated}")
|
||||
else:
|
||||
print(f"{display_name}: {last_updated} (no change)")
|
||||
|
||||
if changes:
|
||||
README_PATH.write_text(updated_content, encoding="utf-8")
|
||||
print("Updated README.md:")
|
||||
for name, date in changes:
|
||||
print(f"- {name}: {date}")
|
||||
else:
|
||||
print("All tracked PyPI dates are current.")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
project_root="$(cd "$script_dir/../../../.." && pwd)"
|
||||
|
||||
cd "$project_root"
|
||||
uv run python "$script_dir/check_pypi_dates.py"
|
||||
uv run python scripts/validate_readme.py
|
||||
Reference in New Issue
Block a user