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.
git clone --depth 1 https://github.com/aeonfun/aeon /tmp/shiplog && cp -r /tmp/shiplog/skills/shiplog ~/.claude/skills/shiplogSKILL.md
> **${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 fetcSet up and run an Aeon agent instance — get started from scratch, pick which skills to turn on or install more from packs, reschedule or change what runs, edit what an existing skill does, fix a skill that isn't firing, set the STRATEGY.md north star and soul/ voice, turn a coding-agent chat into a scheduled Aeon skill, and mine past coding-agent conversations for recurring work worth automating as a skill. Use when the user mentions Aeon, aeon.yml, an Aeon skill / instance / routine / pack, asks to schedule, enable, edit, or debug an agent that runs on a cron, or asks what of their repeated/manual work Aeon could take over.
Mention/keyword sweep on social platforms for [REPLACE: KEYWORDS] — trends, sentiment, top posts
5 concrete real-life actions, leverage-scored against open loops with specificity and anti-fluff gates
Static config-correctness linter for this instance - catches the silent-failure class (unquoted schedules, duplicate keys, unconfigured skills, mode typos, broken requires/MCP refs) that no run-based health skill can see. Notifies only on problems.
Pull framework updates from the upstream Aeon repo into this instance - 3-way merges canon's new commits into a PR, never clobbering operator config.
Write a publication-ready article in one of three angles - a trending long-form piece, a watched-repo thesis, or a project-through-a-lens essay. Optional Replicate hero image with --visual.
Automatically merge open PRs that have passing CI, no blocking reviews, and no conflicts
Two-mode aeon.yml workflow builder - analyze inspects URLs and emits a tiered, signal-verified skill-enablement plan plus an aeon.yml diff; enable flips slugs to enabled:true and opens a PR.