بنقرة واحدة
docs-sync
Sync recently merged product PRs into the marketing website as a changelog — opens a PR on the website repo
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Sync recently merged product PRs into the marketing website as a changelog — opens a PR on the website repo
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Price performance report for the project's token — price, volume, liquidity, and context
Report on new stars, forks, and releases for watched repos — with profile enrichment (name, location, company, bio, follower count) for every new stargazer and forker, plus a one-line growth verdict
Crypto market scanner and single-token analyst - movers scans top winners/losers/trending or on-chain runners with pump-risk flags; single-token produces a verdict-first deep report for one token.
Account-based digest of recent tweets from tracked X/Twitter accounts. Sibling to fetch-tweets (keyword) and tweet-roundup (topic).
| type | Skill |
| name | Docs Sync |
| category | dev |
| description | Sync recently merged product PRs into the marketing website as a changelog — opens a PR on the website repo |
| var | |
| tags | ["dev","content","build"] |
| mode | write |
| commits | false |
| permissions | ["contents:write","pull-requests:write"] |
| requires | ["GH_GLOBAL"] |
${var} — Optional override in
product_repo->website_repoform (e.g.aaronjmars/aeon->aaronjmars/aeon-website). If empty, read config frommemory/docs-sync.md.
Today is ${today}. Your task: take the product's recently merged PRs and publish them as a changelog on the product's marketing website, via a branch + PR on the website repo. The website is the public face — this keeps "what shipped" visible without anyone hand-writing release notes.
This skill is config-driven so the same file works in every instance (aeon → aeon-website, miroshark → miroshark-website). It reads which repos to use from memory/docs-sync.md; it never hardcodes repo names.
Read memory/docs-sync.md. It defines:
product_repo — the repo whose merged PRs become the changelog (e.g. aaronjmars/aeon).website_repo — the Next.js marketing site to update (e.g. aaronjmars/aeon-website).min_prs (optional, default 1) — minimum number of new unpublished PRs required to publish an entry.lookback_days (optional, default 7) — only consider PRs merged within this many days. Bounds each entry to one window so a run never sweeps in months of history; matches the weekly schedule.draft (optional, default true) — open the website PR as a draft.If ${var} is set, it overrides product_repo->website_repo for this run.
If neither ${var} nor memory/docs-sync.md provides both repos, exit with DOCS_SYNC_NO_CONFIG (notify + log, no PR).
Compute the window cutoff first — lookback_days ago (default 7), as an ISO timestamp:
SINCE=$(date -u -d "${LOOKBACK_DAYS:-7} days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-"${LOOKBACK_DAYS:-7}"d +%Y-%m-%dT%H:%M:%SZ)
Then fetch the last 50 closed PRs and keep only those merged within the window, newest merge first:
gh api "repos/${PRODUCT_REPO}/pulls" -X GET -f state=closed -f sort=updated -f direction=desc -f per_page=50 \
--jq "[.[] | select(.merged_at != null) | select(.merged_at > \"$SINCE\") | {number, title, url: .html_url, author: .user.login, merged_at, labels: [.labels[].name], body: (.body // \"\" | .[0:500])}] | sort_by(.merged_at) | reverse"
The window is the primary filter; the published-PR dedup in step 2 is the idempotency guard against overlap and re-runs. Sandbox: if gh api fails transiently, retry once. Never use curl for the GitHub API — gh handles auth.
Clone the website repo and read the existing changelog data:
WORK_DIR="/tmp/docs-sync-work"
rm -rf "$WORK_DIR"
gh repo clone "$WEBSITE_REPO" "$WORK_DIR" -- --depth 20
cd "$WORK_DIR"
git config user.name "aeonframework"
git config user.email "aeonframework@proton.me"
Pin the commit identity in the clone. A freshly cloned repo does NOT inherit the workflow's git identity, so without these two lines the commit author falls back to an improvised/unlinked email (past runs produced aeon@aeon.fun, which links to no GitHub account). Always set it so every changelog commit + PR is attributed to the @aeonframework account (aeonframework@proton.me) — never anything else.
If app/changelog-data.ts exists, read it and collect PUBLISHED_PR_NUMBERS (every PR number already in CHANGELOG). If it doesn't exist yet, this is a bootstrap run (see step 4) and nothing is published.
Compute the new set: from step 1's windowed PRs, keep only those whose number is NOT in PUBLISHED_PR_NUMBERS. PR number is the idempotency key — not dates — so re-running within the same window is always safe and never duplicates.
DOCS_SYNC_NOTHING_NEW (silent: log only, no PR, no notify).0 < count < min_prs → exit DOCS_SYNC_BELOW_THRESHOLD (log only, no PR). Lets PRs accumulate into a meaningful entry.Split the new PRs:
dependabot[bot] and titles starting chore(deps, chore(deps-dev), chore(actions), ci:, build:, style:. These get rolled into a single "Maintenance: N dependency/CI bumps" highlight, not listed individually.prs array so idempotency stays exact — but only the substantive ones get their own highlight bullet.Compose ONE ChangelogEntry:
date: ${today} (YYYY-MM-DD).title: 4–8 words naming the dominant theme of the batch (e.g. "i18n expansion + simulation fixes"). Derive it from the substantive PR titles, not boilerplate. Never "various improvements".summary: 1–2 plain-language sentences — what a builder following the project would care about. No hype, no "we're excited".highlights: one bullet per substantive PR (plus the single maintenance rollup if any). Each bullet ≤ 18 words, names the concrete change, ends with the PR ref (#N). Translate commit-speak into plain English.prs: every new PR as { number, title, url, author }.Banned phrases: "exciting", "robust", "leverage", "unlocks", "seamless", "we're thrilled", "stay tuned". They signal stock release-note filler.
The data file app/changelog-data.ts is the only file you mutate on a normal run. Its shape:
export type ChangelogPR = { number: number; title: string; url: string; author: string };
export type ChangelogEntry = {
date: string; // YYYY-MM-DD
title: string; // 4–8 word theme
summary: string; // 1–2 sentences
highlights: string[];
prs: ChangelogPR[];
};
export const CHANGELOG: ChangelogEntry[] = [
// newest first — PREPEND new entries here, never rewrite existing ones
];
export const PUBLISHED_PR_NUMBERS = CHANGELOG.flatMap((e) => e.prs.map((p) => p.number));
Normal run: prepend the new entry to the top of the CHANGELOG array. Touch nothing else.
Bootstrap run (no app/changelog-data.ts yet) — create the changelog surface, matching the site's existing conventions (do NOT invent a new design system):
app/changelog-data.ts with the schema above + your first entry.app/changelog/page.tsx that renders CHANGELOG. Read an existing list page first (app/blog/page.tsx is the model on these sites) and reuse its shared chrome: same SiteNav/SiteFooter, the same CSS module it imports (e.g. ../docs/page.module.css as chrome), the same hero/section structure. Wire full Next.js metadata (title, description, canonical, OpenGraph) like the other pages. Give it a JSON-LD block if the blog page has one.app/docs/page.tsx: import CHANGELOG from ../changelog-data and render the latest 3 entries inline, with a "Full changelog →" link to /changelog. Place it near the top of the docs body, after the intro. Keep edits to that file minimal and self-contained.changelog link to the primary nav in app/site-chrome.tsx (or wherever the site renders its nav — check the layout if there's no site-chrome).Match indentation, quote style, and naming of each repo exactly. After editing, if the site has a typecheck/lint/build available and the sandbox allows it, run it (npm run lint / npx tsc --noEmit / npm run build) and fix any error your change introduced. If the sandbox blocks npm, skip silently — note it in the PR body.
BRANCH="aeon/changelog-${today}"
git checkout -b "$BRANCH"
git add -A
git commit -m "docs(changelog): sync N merged PRs from ${PRODUCT_REPO}"
git push -u origin "$BRANCH"
Open the PR on the website repo (draft unless config says otherwise):
gh pr create --repo "$WEBSITE_REPO" --draft \
--title "docs(changelog): ${today} — <entry title>" \
--body "$(cat <<'EOF'
## Summary
Auto-generated changelog sync from merged PRs in `${PRODUCT_REPO}`.
## Entry
**<title>** — <summary>
## PRs included
- #N — title (@author)
- ...
---
Generated by the aeon `docs-sync` skill. Review and merge to publish.
EOF
)"
Use --draft when draft config is true (the default). Build the PR body from the real entry — never leave placeholders.
*Docs Sync — ${today}*
${PRODUCT_REPO} → ${WEBSITE_REPO}
N new PRs → changelog entry "<title>"
Append to memory/logs/${today}.md:
### docs-sync
- Status: DOCS_SYNC_OK | DOCS_SYNC_BOOTSTRAP | DOCS_SYNC_NOTHING_NEW | DOCS_SYNC_BELOW_THRESHOLD | DOCS_SYNC_NO_CONFIG
- Product: ${PRODUCT_REPO} → Website: ${WEBSITE_REPO}
- New PRs: N (numbers: ...)
- Entry: "<title>"
- PR: <url>
GitHub Actions runs Claude Code in a non-interactive sandbox.
gh api / gh pr create / gh repo clone — never curl (auth + sandbox). gh works because it handles auth internally.&&, ||, |, ;) and $(...)/$VAR expansion in skill bash blocks. Split into separate calls; the working directory persists, so cd "$WORK_DIR" as its own call then run commands. Compute literal values (repo names, branch) in your reasoning, not via shell substitution.npm run build/lint fails to reach the network or is denied, skip it and note "build not verified in sandbox" in the PR body rather than aborting.GH_GLOBAL (a token with cross-repo write to the website repo). GITHUB_TOKEN alone only covers the current repo and cannot push to the website.PUBLISHED_PR_NUMBERS. Re-running must be a no-op when nothing new merged.(#N). No invented activity.