Skip to main content

shiplog

Recap of everything shipped since the last run - cross-repo PRs, security fixes, star deltas, and X traction, synthesized into a digest article and a ready-to-post shiplog in your voice.

インストールへ移動

ソース情報

リポジトリ
aeonfun/aeon
ソースの最終更新活動
2026年8月17日 22:02
検出された SKILL.md の言語
英語
スター
738
フォーク
265

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
shiplog
description
Recap of everything shipped since the last run - cross-repo PRs, security fixes, star deltas, and X traction, synthesized into a digest article and a ready-to-post shiplog in your voice.
metadata
{"title":"Shiplog","category":"core","var":"","requires":["XAI_API_KEY?","GH_GLOBAL?"],"tags":["content","social"]}
> **${var}** — Optional, space-separated flags: > - `since:YYYY-MM-DD` — override the window start (default: when this skill last ran). > - `days:N` — window = last N days. > - `dry-run` — render to stdout; write no article, no state, no notify. > - `owner/repo` — narrow GitHub coverage to that one repo. > - any other word — focus/theme filter (a product name or keyword). > > Empty = everything shipped since the last run, across all configured repos. Produce two artifacts: 1. **Digest** — a themed, human-readable recap of everything that shipped + traction (the article). 2. **Shiplog post** — a tight, bulleted, ready-to-post version in the operator's voice, every project @-tagged (the notification). This is **cadence-agnostic**: the window is always "since the last run" (`memory/state/shiplog-last.json`), so the `aeon.yml` schedule alone decides whether this is a daily, weekly, or on-demand recap. One skill, any frequency. Read `STRATEGY.md`, `memory/MEMORY.md`, and the last 7 days of `memory/logs/` for context. Read `soul/SOUL.md` + `soul/STYLE.md` before writing any output — the shiplog post must sound like the operator, not a changelog bot. **If `soul/` is empty, use a clear, direct, neutral voice** (drop the signature flourishes below). ## Config — all derived, nothing hardcoded ``` operator = gh api user --jq .login # the authenticated operator (PR-author search) product_handles = memory/products.md `handles:` lines (@x) # product X accounts to read flagship_repos = memory/products.md `repos:` tagged (public) # the star / north-star story watched_repos = memory/watched-repos.md, else products.md repos: # everything shipped across ecosystem_scouts= memory/products.md `scouts:` line (optional) # recap accounts to scan for features star_state = memory/state/shiplog-stars.json # snapshot for week-over-week star deltas ``` If neither `watched-repos.md` nor `products.md` yields a repo, exit `SHIPLOG_NO_REPOS` (notify + log, no article). Sections whose config is absent (X handles, scouts) are **skipped gracefully**, not failed. ## Steps ### 1. Compute the window — "since last run" ```bash STATE="memory/state/shiplog-last.json" NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ) TODAY=$(date -u +%Y-%m-%d) LAST="" [ -f "$STATE" ] && LAST=$(jq -r '.last_run_at // empty' "$STATE" 2>/dev/null) SINCE="${LAST:-$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-7d +%Y-%m-%dT%H:%M:%SZ)}" SINCE_DATE="${SINCE%%T*}" ``` - `since:YYYY-MM-DD` in `${var}` → `SINCE` = that date at `T00:00:00Z`; `days:N` → N days ago. These override the state file. - Use `$SINCE` for ALL time filtering — never substitute "since Monday" or other drift-prone shortcuts. The window is `[$SINCE, $NOW)`; state the span (`$SINCE_DATE → $TODAY`) in the output. - **Idempotency is the state file** (step 8 advances it each run, so windows never overlap). No once-per-day lock — a back-to-back re-run just yields an empty window → `SHIPLOG_NOTHING_NEW`. Write the digest to `output/articles/shiplog-${TODAY}.md`; if that name exists and there's genuinely new activity since the last run, use `output/articles/shiplog-${TODAY}-2.md` rather than clobbering. ### 2. GitHub activity (the bytes) Cross-repo PR/commit visibility needs the global token — the built-in `GITHUB_TOKEN` only sees this repo. Prefer `GH_GLOBAL` when set: ```bash GHT="${GH_GLOBAL:-$GITHUB_TOKEN}" # gh reads GH_TOKEN from env; falls back to the repo-scoped token OPERATOR=$(GH_TOKEN="$GHT" gh api user --jq .login 2>/dev/null) ``` Track success/failure per source in a `sources` map; on a single endpoint failure log `fail` and continue — never abort the whole skill. **a) Operator PRs across all repos in the window** (grouped by repo + totals): ```bash GH_TOKEN="$GHT" gh search prs --author "$OPERATOR" --created ">=$SINCE_DATE" \ --json number,title,repository,state,createdAt,url --limit 100 \ --jq 'group_by(.repository.nameWithOwner)[] | {repo: .[0].repository.nameWithOwner, count: length, prs: [.[] | {date: .createdAt[0:10], state, number, title}]}' ``` If 100 rows come back, note the result may be truncated. **b) Flagship headline numbers** — for each `flagship_repos` entry, count commits + merged PRs in the window (the numbers the audience cares about): ```bash for REPO in $FLAGSHIP_REPOS; do GH_TOKEN="$GHT" gh api "repos/${REPO}/commits" -X GET -f since="$SINCE" \ --jq "\"$REPO commits: \" + ([.[] | .sha] | length | tostring)" 2>/dev/null GH_TOKEN="$GHT" gh api "repos/${REPO}/pulls" -X GET -f state=closed -f sort=updated -f direction=desc \ --jq "\"$REPO merged PRs: \" + ([.[] | select(.merged_at != null and .merged_at > \"$SINCE\")] | length | tostring)" 2>/dev/null done ``` **c) The security flex** — PRs the operator landed in repos they do NOT own (the "a project merged a fix from us" candidates). Filter the Step-2a result to external repos whose title matches a security keyword (`security|ssrf|cve|credential|sandbox|escape|injection|vuln|redos|xss|toctou|path traversal|prototype pollution|deserial`). Merged ones are the marquee story — if it's a named org (not a random fork), that's a headline bullet. **d) Star delta** (north-star metric — flagships only, they're public): ```bash mkdir -p memory/state for REPO in $FLAGSHIP_REPOS; do GH_TOKEN="$GHT" gh api "repos/${REPO}" --jq '.stargazers_count' # current total for $REPO done ``` Read the prior snapshot `memory/state/shiplog-stars.json` (if present): `delta = current_total − last_total` per repo. After computing, overwrite the snapshot with `{ "<repo>": {"count": N, "date": "${TODAY}"}, ... }`. If no prior snapshot exists, report totals only and note "no baseline yet — deltas start next run." Do NOT fabricate a delta. ### 3. X activity (direct X.AI curl — primary) `XAI_API_KEY` is **injected into this skill's environment** (declared in `requires:`) and is the primary way to read X. **For each X source below the primary fetch is a direct `curl` to `https://api.x.ai/v1/responses` with `Authorization: Bearer {XAI_API_KEY}`**, using Grok's `x_search` tool. There is no network sandbox blocking this — just make the calls. **Check the key and give the call room first:** ```bash [ -n "$XAI_API_KEY" ] && echo KEY_PRESENT || echo KEY_UNSET ``` `x_search` runs a live X search and typically takes 30–120s. When you invoke the Bash tool for any curl below, **set the tool's `timeout` to at least 180000 (180s)**; each curl carries `--max-time 150` so it fails cleanly instead of hanging. **A slow curl is not a missing key — never treat a timeout as key-unavailable.** If `KEY_PRESENT` (it will be), Path A is required for every X source. There are two X sources here — the **operator** handle (`$OPERATOR_HANDLE`, the operator's own X account from `memory/products.md`) and the **product/project** accounts (`$PRODUCT_HANDLES`). Fetch each independently into its own tmp file so one failing source can't clobber or sink another. **Path A — X.AI API (primary).** *Operator posts* (`SRC=operator`): ```bash jq -n --arg h "$OPERATOR_HANDLE" --arg sd "$SINCE_DATE" --arg td "$TODAY" '{model:"grok-4.6", input:[{role:"user", content:("Search X for posts by @" + $h + " between " + $sd + " and " + $td + ". Return each post with full text, date, type (original|reply|RT — an RT text starts with \"RT @\"), exact engagement counts (likes, retweets, replies; 0 if unknown), and the direct link https://x.com/" + $h + "/status/ID. Return chronological.")}], tools:[{type:"x_search"}]}' > /tmp/xai-shiplog-operator-payload.json HTTP=$(./secretcurl -s -o /tmp/xai-shiplog-operator.json -w '%{http_code}' --max-time 150 -X POST "https://api.x.ai/v1/responses" \ -H "Content-Type: application/json" -H "Authorization: Bearer {XAI_API_KEY}" -d @/tmp/xai-shiplog-operator-payload.json) echo "xai http=$HTTP bytes=$(wc -c </tmp/xai-shiplog-operator.json)" ``` *Product/project accounts* (`SRC=projects`): ```bash jq -n --arg sd "$SINCE_DATE" --arg td "$TODAY" --arg ph "$PRODUCT_HANDLES" '{model:"grok-4.6", input:[{role:"user", content:("Search X for posts between " + $sd + " and " + $td + " from these accounts: " + $ph + ". Focus on launches, announcements, and any brag about a security fix merged into another project. For each: @handle, full text, date, exact engagement counts (likes, retweets, replies; 0 if unknown), and the direct link https://x.com/handle/status/ID. Skip retweets of others.")}], tools:[{type:"x_search"}]}' > /tmp/xai-shiplog-projects-payload.json HTTP=$(./secretcurl -s -o /tmp/xai-shiplog-projects.json -w '%{http_code}' --max-time 150 -X POST "https://api.x.ai/v1/responses" \ -H "Content-Type: application/json" -H "Authorization: Bearer {XAI_API_KEY}" -d @/tmp/xai-shiplog-projects-payload.json) echo "xai http=$HTTP bytes=$(wc -c </tmp/xai-shiplog-projects.json)" ``` For each source, on `HTTP=200` with a non-empty body, parse that source's file with the standard extractor and mark `x_source=api`: ```bash jq -r '.output[] | select(.type == "message") | .content[] | select(.type == "output_text") | .text' /tmp/xai-shiplog-<SRC>.json ``` From the **operator** text, separate **original posts** from **RTs** (RT text starts with `RT @`) — RTs are amplification, not ships. From the **projects** text, note the bangers (sort by likes/views) — one or two feed the digest's narrative section. **On a real failure, skip that source — never fabricate posts.** If a source's curl returns non-200, an empty/unparseable body, or times out, record the **true reason** for that source and continue with whatever other sources succeeded. Reason codes: `key-unset` (only if the check above printed `KEY_UNSET`), `http-<code>` (non-2xx), `empty` (200 but no posts parsed), `timeout` (exceeded `--max-time`). Never write "XAI_API_KEY unavailable" when the key was set. **Path B — WebFetch last resort (per source, optional).** Only if a source's Path A failed for one of the real reasons above: WebFetch that source's public `https://x.com/<handle>` profile(s) — no auth — and mark that source `x_source=webfetch` (lower quality; prefer posts inside the window). If every X source fails both paths, set `x_source=none` and write the GitHub-only shiplog (note the gap) — **never abort**. ### 4. Ecosystem + traction sweep (best-effort — skip gracefully) - **Ecosystem mentions** — only if `ecosystem_scouts` (`scouts:`) is configured. Fetch with the same **Path A** X.AI curl as Step 3, into its own tmp file (`SRC=ecosystem`): ```bash jq -n --arg sd "$SINCE_DATE" --arg td "$TODAY" --arg es "$ECOSYSTEM_SCOUTS" --arg ph "$PRODUCT_HANDLES" '{model:"grok-4.6", input:[{role:"user", content:("Search X between " + $sd + " and " + $td + " for posts from these recap/scout accounts: " + $es + " that mention any of these products: " + $ph + ". Return each mention with @handle, follower_count, full text, date, and the direct link https://x.com/handle/status/ID — recaps, rankings, partner shares.")}], tools:[{type:"x_search"}]}' > /tmp/xai-shiplog-ecosystem-payload.json HTTP=$(./secretcurl -s -o /tmp/xai-shiplog-ecosystem.json -w '%{http_code}' --max-time 150 -X POST "https://api.x.ai/v1/responses" \ -H "Content-Type: application/json" -H "Authorization: Bearer {XAI_API_KEY}" -d @/tmp/xai-shiplog-ecosystem-payload.json) echo "xai http=$HTTP bytes=$(wc -c </tmp/xai-shiplog-ecosystem.json)" ``` On `HTTP=200` + non-empty, parse `/tmp/xai-shiplog-ecosystem.json` with the standard extractor. On a real curl failure (`http-<code>` / `empty` / `timeout`, or `key-unset`) skip this source with the true reason — **never fabricate a mention**. Confirm any handle is real before @-mentioning (a wrong tag in a public post is worse than none). Capture follower counts for the flex ("featured by @X (Nk)"). Skip entirely if no `scouts:` configured. - **Product traction** (OpenRouter / x402 / analytics) — only if a source is configured for the product. If you have an app/server id, WebFetch its page; otherwise say "no product-traction sources wired yet" and move on. Keep any number exactly as measured — don't round 79 → ~80.
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る