
Storybook Sync
- 1 installs
- 155 repo stars
- Updated August 3, 2026
- rstackjs/storybook-rsbuild
Helps with ai & agent building tasks during AI-assisted development.
About
storybook-sync is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- storybook-sync
- AI & Agent Building
- AI-coding skill
Storybook Sync by the numbers
- 1 all-time installs (skills.sh)
- Ranked #14,102 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/rstackjs/storybook-rsbuild --skill storybook-syncAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 155 |
| Last updated | August 3, 2026 |
| Repository | rstackjs/storybook-rsbuild ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Storybook Upstream Sync Checker
Analyze recent changes in the official storybookjs/storybook repository and identify which ones may need to be synced to this storybook-rsbuild repo. The storybook-rsbuild project adapts Storybook's official builder and framework packages to work with Rsbuild, so when upstream changes builder-webpack5, builder-vite, or framework integrations, those changes may need to be reflected here.
Upstream to Local Package Mapping
+----------------------------------------+--------------------------------------+
| Upstream (storybookjs/storybook) | Local (storybook-rsbuild) |
+----------------------------------------+--------------------------------------+
| code/builders/builder-webpack5 | packages/builder-rsbuild |
| code/builders/builder-vite | packages/builder-rsbuild |
| code/frameworks/react-vite | packages/framework-react |
| code/frameworks/react-webpack5 | packages/framework-react |
| code/frameworks/vue3-vite | packages/framework-vue3 |
| code/frameworks/web-components-vite | packages/framework-web-components |
| code/lib/core-webpack | packages/builder-rsbuild (prebundled)|
+----------------------------------------+--------------------------------------+Both webpack5 and vite upstream variants are monitored because this repo borrows patterns from both.
Upstream Commit History is Noisy
The Storybook repo uses a non-linear branching model with frequent merge commits and automated version bumps. The bundled script filters the most common noise automatically (version bump commits via --invert-grep, merge commits via --no-merges). Some noise may still slip through — NX upgrades, CI config, non-standard version bumps, reverts that cancel out.
Always judge from the actual diff. Storybook does not consistently follow conventional commits, so commit messages are unreliable for triage decisions.
Sync Priority Criteria
High — sync soon:
- Bug fixes in logic that was adapted into storybook-rsbuild
- Security patches
- API / type / interface changes (options, preset signatures, exports)
- Breaking changes or deprecations
Medium — review and decide:
- New features that could benefit storybook-rsbuild users
- Significant refactoring of adapted code patterns
- Performance improvements in shared logic
Low — nice to know:
- Minor code quality improvements
- Added error handling or edge-case guards
- Test changes that reveal expected behavioral contracts
Skip:
- Webpack/Vite internal plumbing with no Rsbuild parallel (e.g. webpack plugin hooks, Vite-specific HMR wiring, Vite module graph internals)
- Documentation-only changes
- CI/tooling changes internal to the Storybook repo
- Changes to
storybook/internal/*APIs (these arrive via thestorybooknpm dependency, not by manual sync) - Pure test file additions with no behavioral insight
- Build system changes (NX, workspace config, import rewriting) that are specific to the Storybook monorepo structure
Workflow
1. Preparation
Generate the report filename (anchored to system clock):
REPORT_NAME=$(bash <skill-dir>/scripts/fetch_upstream.sh --report-name)Determine the commit range from the user's request:
- Relative days: "past 20 days", "last 30 days" → use
--days N - Absolute date range: "since 2025-12-01", "Dec 1 to Dec 20" → use
--since/--until - Version tags: "between v8.4.0 and v8.5.0" → use
--from/--to - If unspecified, default to
--days 30.
Important: For relative date ranges, always use --days N. The script reads the system clock to compute exact dates, avoiding date miscalculation.
2. Get commit summary and decide strategy
Fetch the commit list with diff line counts:
bash <skill-dir>/scripts/fetch_upstream.sh --summary --days <N>Output: HASH|DATE|AUTHOR|SUBJECT|LINES_ADDED+LINES_DELETED (one per line, oldest first — the script uses --reverse).
Capture the range bounds from the summary output — the first line's hash is START_SHA (oldest commit in range), the last line's hash is END_SHA (newest). These are the actual commits the report covers, regardless of whether the user asked for a date range, tag range, or commit range.
This is critical for reproducibility: END_SHA is exactly where the next sync run should start from, and START_SHA anchors the beginning to a precise ref even when the user specified a fuzzy bound like --days 30 or --since 2026-03-12.
Based on the commit count:
- ≤ 8 commits → step 3a (direct analysis)
- > 8 commits → step 3b (subagent analysis)
3a. Direct analysis (≤ 8 commits)
bash <skill-dir>/scripts/fetch_upstream.sh --diff-all --days <N>For each commit in the output: 1. Read the diff — this is the ground truth. Never skip a commit based on its message or file list alone. 2. Read the corresponding local source file using the package mapping table above. 3. Classify using the sync priority criteria above. 4. Check for revert chains — if a commit and its revert both appear, check if the net effect is zero. If so, classify both as skip.
Then proceed to step 4.
3b. Subagent analysis (> 8 commits)
Plan batches using the --summary output:
1. Sum the total lines changed across all commits. 2. Target 3–5 subagents. Calculate: target_lines_per_batch = total_lines / batch_count. 3. Walk through the commit list in order. Accumulate commits into the current batch. When the accumulated lines exceed the target, start a new batch. Keep adjacent commits together when possible — they are often related.
Spawn subagents — one per batch. Launch all Agent calls in a single message without run_in_background so they execute in parallel as foreground calls. You will receive all results at once when they complete. Do NOT use run_in_background, do NOT sleep or poll.
Use this prompt template for each subagent (note --no-fetch — the primary agent already fetched in step 2):
Analyze upstream Storybook commits for sync relevance to storybook-rsbuild.
storybook-rsbuild adapts Storybook's builder and framework packages for Rsbuild.
When upstream changes their builder or framework code, those changes may need
to be reflected in storybook-rsbuild.
Package mapping (upstream → local):
code/builders/builder-webpack5 → packages/builder-rsbuild
code/builders/builder-vite → packages/builder-rsbuild
code/frameworks/react-vite → packages/framework-react
code/frameworks/react-webpack5 → packages/framework-react
code/frameworks/vue3-vite → packages/framework-vue3
code/frameworks/web-components-vite → packages/framework-web-components
code/lib/core-webpack → packages/builder-rsbuild (prebundled)
Steps:
1. Run: bash <skill-dir>/scripts/fetch_upstream.sh --no-fetch --diff-all --hashes <COMMA_SEPARATED_HASHES>
2. For each commit, read its diff carefully — this is the ground truth.
Commit messages are often inaccurate; always judge from the actual diff.
3. Read the corresponding local source file in storybook-rsbuild for comparison.
4. Classify each commit and return the results in the exact format below.
Priority criteria:
high — bug fix in adapted code, security patch, API/type change, breaking change
medium — new feature worth adopting, significant refactoring of adapted patterns
low — minor improvement, added error handling, test revealing behavioral contract
skip — Vite/webpack internals with no Rsbuild parallel, docs, CI, storybook/internal API changes
Return format (one block per commit, separated by ---):
COMMIT: <full hash>
PRIORITY: high|medium|low|skip
UPSTREAM: <upstream package path, e.g. builders/builder-webpack5>
LOCAL: <local package path, e.g. packages/builder-rsbuild>
SUBJECT: <commit subject>
DATE: <YYYY-MM-DD>
AUTHOR: <author name>
WHAT_CHANGED: <1-2 sentence summary of the actual code change>
REASON: <why sync is needed, or why it can be skipped>
KEY_FILES: <comma-separated list of relevant changed files>
---Aggregate results: Collect all subagent responses. Group commits by priority level. For revert chains where both the original and revert appear, check if the net effect is zero — if so, move both to skip.
4. Write the report
Save to $REPORT_NAME in the project root.
# Storybook Upstream Sync Report
- **Range**: <range-label> ([`<START_SHA_SHORT>`](https://github.com/storybookjs/storybook/commit/<START_SHA>) → [`<END_SHA_SHORT>`](https://github.com/storybookjs/storybook/commit/<END_SHA>))
- **Generated**: YYYY-MM-DD
- **Upstream branch**: next
- **Commits scanned**: N (after filtering out version bumps and merges)
- **Needs attention**: X (H high, M medium, L low)
---
## High Priority
### [`abcdef0`](https://github.com/storybookjs/storybook/commit/FULL_HASH) commit subject here
- **Date**: YYYY-MM-DD | **Author**: name
- **Upstream package**: builders/builder-webpack5
- **Local package**: packages/builder-rsbuild
- **What changed**: 1-2 sentence summary of the actual code change.
- **Why sync**: Explanation of why this matters for storybook-rsbuild.
- **Key files**: list of relevant changed files
---
## Medium Priority
(same format as High)
## Low Priority
(briefer format — one paragraph per commit is sufficient)
## Skipped
(bullet list: `short-hash` subject — reason for skipping)Commits within each priority section should be in chronological order (oldest first).
Range line: always pin both ends to precise linked SHAs — never leave "HEAD" or a bare date. Use START_SHA and END_SHA from step 2 (the first and last hashes of the --summary output). The <range-label> is a human-readable description of how the user specified the range:
--from v10.0.0 --to v10.1.0→v10.0.0..v10.1.0 ([\abc1234\](...) → [\def5678\](...))--from v10.0.0(open end) →v10.0.0..next ([\abc1234\](...) → [\def5678\](...))--since 2026-03-12 --until 2026-04-11→2026-03-12..2026-04-11 ([\abc1234\](...) → [\def5678\](...))--days 30→last 30 days (2026-03-12..2026-04-11) ([\abc1234\](...) → [\def5678\](...))
This precision is critical for follow-up syncs — END_SHA becomes the exact starting ref (--from <END_SHA>) for the next run, regardless of whether the range ended at HEAD, a tag, or a past date.
5. Offer to create an issue
After writing the report, ask the user if they want to publish it as a GitHub issue in this repository. If yes, create the issue using gh:
gh issue create --title "<TITLE>" --body-file "$REPORT_NAME" --label "storybook sync report"Title format: Storybook Sync: <range> — where <range> matches the range used in the report. Examples:
- Date range:
Storybook Sync: 2026-03-12 – 2026-04-11 - Version range:
Storybook Sync: v8.4.0 – v8.5.0
#!/usr/bin/env bash
# Fetch and list upstream Storybook commits affecting monitored packages.
# Maintains a blobless clone cache at ~/.cache/storybook-upstream/.
#
# Noise filtering:
# - Merge commits are excluded (--no-merges)
# - "Bump version from ..." commits are excluded (--invert-grep)
# - The upstream branch is `next` (Storybook's primary development branch)
set -euo pipefail
CACHE_DIR="${HOME}/.cache/storybook-upstream"
REPO_URL="https://github.com/storybookjs/storybook.git"
UPSTREAM_BRANCH="next"
# Monitored upstream paths — edit this list to track different packages.
# These use the `code/` prefix matching the Storybook 8.x+ monorepo layout.
PATHS=(
"code/builders/builder-webpack5"
"code/builders/builder-vite"
"code/frameworks/react-vite"
"code/frameworks/react-webpack5"
"code/frameworks/vue3-vite"
"code/frameworks/web-components-vite"
"code/lib/core-webpack"
)
SINCE=""
UNTIL=""
DAYS=""
FROM_REF=""
TO_REF=""
DIFF_HASH=""
FILES_HASH=""
DIFF_ALL=false
SUMMARY=false
FILTER_HASHES=""
NO_FETCH=false
while [[ $# -gt 0 ]]; do
case $1 in
--since) SINCE="$2"; shift 2 ;;
--until) UNTIL="$2"; shift 2 ;;
--days) DAYS="$2"; shift 2 ;;
--from) FROM_REF="$2"; shift 2 ;;
--to) TO_REF="$2"; shift 2 ;;
--diff) DIFF_HASH="$2"; shift 2 ;;
--files) FILES_HASH="$2"; shift 2 ;;
--diff-all) DIFF_ALL=true; shift ;;
--summary) SUMMARY=true; shift ;;
--hashes) FILTER_HASHES="$2"; shift 2 ;;
--no-fetch) NO_FETCH=true; shift ;;
--report-name) echo "upstream-sync-report-$(date +%Y%m%d-%H%M%S).md"; exit 0 ;;
-h|--help)
cat <<'HELP'
Usage: fetch_upstream.sh [OPTIONS]
Range options (shared across modes):
--days N Look back N days from today (uses system clock, recommended)
--since DATE Start date (e.g. 2025-12-01)
--until DATE End date (defaults to today)
--from REF Start ref/tag (e.g. v8.4.0)
--to REF End ref/tag (e.g. v8.5.0)
Modes:
(default) List commits: HASH|DATE|AUTHOR|SUBJECT
--summary List commits with diff line counts: HASH|DATE|AUTHOR|SUBJECT|LINES
--diff-all Output metadata + file list + diff for every commit in range
--diff HASH Show diff for one commit (monitored paths only)
--files HASH List monitored files changed by one commit
--report-name Print a timestamped report filename and exit
Filtering:
--hashes H1,H2 Limit --diff-all or --summary to specific commits (skip range query)
--no-fetch Skip git fetch (use when cache was already updated this session)
Noise filtering (applied automatically):
- Merge commits are excluded
- "Bump version from ..." commits are excluded
- Default upstream branch: next
HELP
exit 0
;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
esac
done
# ── Resolve --days into --since/--until from system clock ─────────────
if [ -n "$DAYS" ]; then
UNTIL=$(date +%Y-%m-%d)
# macOS date syntax, with GNU fallback
SINCE=$(date -v-"${DAYS}"d +%Y-%m-%d 2>/dev/null || date -d "${DAYS} days ago" +%Y-%m-%d)
echo ":: System time: $(date '+%Y-%m-%d %H:%M:%S %Z')" >&2
echo ":: Resolved --days $DAYS → --since $SINCE --until $UNTIL" >&2
fi
# ── Ensure cache ──────────────────────────────────────────────────────
if [ -d "$CACHE_DIR/.git" ]; then
if [ "$NO_FETCH" = false ]; then
echo ":: Fetching latest upstream ($UPSTREAM_BRANCH)..." >&2
git -C "$CACHE_DIR" fetch --all --tags --prune 2>/dev/null
fi
else
echo ":: First run — cloning storybookjs/storybook (blobless, ~1-2 min)..." >&2
mkdir -p "$(dirname "$CACHE_DIR")"
git clone --filter=blob:none --no-checkout "$REPO_URL" "$CACHE_DIR" 2>&1 | tail -1 >&2
fi
# ── Single-commit: show diff ─────────────────────────────────────────
if [ -n "$DIFF_HASH" ]; then
git -C "$CACHE_DIR" show "$DIFF_HASH" -- "${PATHS[@]}"
exit 0
fi
# ── Single-commit: list files ────────────────────────────────────────
if [ -n "$FILES_HASH" ]; then
git -C "$CACHE_DIR" diff-tree --no-commit-id --name-only -r "$FILES_HASH" -- "${PATHS[@]}"
exit 0
fi
# ── Build range args (shared by list, summary, and diff-all) ─────────
RANGE_ARGS=()
if [ -n "$FROM_REF" ] && [ -n "$TO_REF" ]; then
RANGE_ARGS+=("${FROM_REF}..${TO_REF}")
elif [ -n "$FROM_REF" ]; then
RANGE_ARGS+=("${FROM_REF}..origin/${UPSTREAM_BRANCH}")
else
RANGE_ARGS+=("origin/${UPSTREAM_BRANCH}")
fi
build_log_cmd() {
LOG_CMD=(git -C "$CACHE_DIR" log)
LOG_CMD+=(--no-merges)
LOG_CMD+=(--invert-grep --grep='Bump version from')
LOG_CMD+=(--reverse)
[ -n "$SINCE" ] && LOG_CMD+=("--since=${SINCE}")
[ -n "$UNTIL" ] && LOG_CMD+=("--until=${UNTIL}")
}
# ── Resolve hash list (from --hashes or git log) ─────────────────────
resolve_hashes() {
if [ -n "$FILTER_HASHES" ]; then
echo "$FILTER_HASHES" | tr ',' '\n'
else
build_log_cmd
LOG_CMD+=("--pretty=format:%H")
LOG_CMD+=("${RANGE_ARGS[@]}")
LOG_CMD+=("--" "${PATHS[@]}")
"${LOG_CMD[@]}" 2>/dev/null || true
fi
}
# ── Summary mode ─────────────────────────────────────────────────────
if [ "$SUMMARY" = true ]; then
HASHES=$(resolve_hashes)
if [ -z "$HASHES" ]; then
echo ":: No commits found in the specified range for monitored paths." >&2
exit 0
fi
COUNT=$(echo "$HASHES" | wc -l | tr -d ' ')
echo ":: Found $COUNT commit(s)" >&2
while IFS= read -r hash; do
META=$(git -C "$CACHE_DIR" log -1 --pretty=format:"%H|%ai|%an|%s" "$hash")
LINES=$(git -C "$CACHE_DIR" diff-tree --no-commit-id --numstat -r "$hash" -- "${PATHS[@]}" \
| awk '{ a += $1; d += $2 } END { print a + 0 "+" d + 0 }')
echo "${META}|${LINES}"
done <<< "$HASHES"
exit 0
fi
# ── Batch diff mode ──────────────────────────────────────────────────
if [ "$DIFF_ALL" = true ]; then
HASHES=$(resolve_hashes)
if [ -z "$HASHES" ]; then
echo ":: No commits found in the specified range for monitored paths." >&2
echo ":: Tip: verify the path prefix matches this Storybook version:" >&2
echo ":: git -C $CACHE_DIR ls-tree --name-only -d origin/$UPSTREAM_BRANCH" >&2
exit 0
fi
COUNT=$(echo "$HASHES" | wc -l | tr -d ' ')
echo ":: Outputting metadata + diff for $COUNT commit(s)..." >&2
IDX=0
while IFS= read -r hash; do
IDX=$((IDX + 1))
# Metadata line
META=$(git -C "$CACHE_DIR" log -1 --pretty=format:"%H|%ai|%an|%s" "$hash")
# Files touched (monitored paths only)
FILES=$(git -C "$CACHE_DIR" diff-tree --no-commit-id --name-only -r "$hash" -- "${PATHS[@]}")
# Diff (monitored paths only, suppress commit header since we print our own)
DIFF=$(git -C "$CACHE_DIR" show --pretty=format:"" "$hash" -- "${PATHS[@]}")
echo "════════════════════════════════════════════════════════════════"
echo "COMMIT $IDX/$COUNT"
echo "$META"
echo "FILES:"
echo "$FILES" | sed 's/^/ /'
echo "DIFF:"
echo "$DIFF"
echo ""
done <<< "$HASHES"
exit 0
fi
# ── List mode (default) ──────────────────────────────────────────────
build_log_cmd
LOG_CMD+=("--pretty=format:%H|%ai|%an|%s")
LOG_CMD+=("${RANGE_ARGS[@]}")
LOG_CMD+=("--" "${PATHS[@]}")
RESULT=$("${LOG_CMD[@]}" 2>/dev/null || true)
if [ -z "$RESULT" ]; then
echo ":: No commits found in the specified range for monitored paths." >&2
echo ":: Tip: verify the path prefix matches this Storybook version:" >&2
echo ":: git -C $CACHE_DIR ls-tree --name-only -d origin/$UPSTREAM_BRANCH" >&2
exit 0
fi
COUNT=$(echo "$RESULT" | wc -l | tr -d ' ')
echo ":: Found $COUNT commit(s) touching monitored packages (after filtering bumps and merges)" >&2
echo "$RESULT"