update-app
GitHub处理AltGallery中应用的仓库迁移或重命名,通过git mv保留历史,重新提取元数据并更新配置文件。
触发场景
安装
npx skills add bebound/AltGallery --skill update-app -g -y
SKILL.md
Frontmatter
{
"name": "update-app",
"description": "Handle a repo migration or app rename for an app already in AltGallery. The user supplies the existing app folder name and the new repo URL; Claude moves the folder with git mv (preserving history), re-extracts the project metadata from the new repo (AltStore source \/ README \/ release ipa — description, subtitle, bundle id, developer, icon, screenshots), re-checks the tag\/filename-derived version against the release ipa (version_source = ipa when they differ), updates config.toml (repo, URLs, tint, min_os_version, asset_pattern), deletes and rewrites news.toml from the template when the icon changed (none of the old color pins survive the new artwork) or updates it in place when it did not, always re-renders images\/news.png, regenerates apps.json, and updates the README entry. Use when an app's GitHub repo moves to a new owner\/repo, the app is renamed, or both."
}
Updating an App: Repo Migration or Rename
An app already in the gallery changed its GitHub repo (new owner, new repo
name, or both) and possibly its display name. The goal is to migrate the
existing folder in git — keeping its history — and then refresh every piece of
metadata from the new repo, exactly as /add-app would extract it for a
new app.
This skill only covers what differs from /add-app; the shared details
(extracting fields from an AltStore source, reading a bundle id / icon from an
ipa, tint sampling, news rendering) are in that skill and in AGENTS.md.
Inputs
Ask for whichever is missing before touching the tree:
- Old app name — the existing folder under
apps/(e.g.PiliPlus), case-sensitive. - New repo URL —
https://github.com/<owner>/<repo>(a bareowner/repois fine).
Then confirm the folder exists:
ls -d apps/<OldName>
If it is not there, list apps/ and match case-insensitively — never guess
which app was meant.
Procedure
-
Resolve the new name
Read the new repo's metadata first:
curl -sL "https://api.github.com/repos/<owner>/<repo>" \ | jq '{full_name, default_branch, description, homepage, archived}'Derive the display name, in this order:
apps[0].namefrom the new repo's own AltStore source (locate it the same way as/add-appstep 2 — README JSON links,altstore/folder, or a dedicated branch);- the app name used in the new README / latest release title;
- the repo name.
The folder name normally mirrors the new repo name or the display name, matching the repo's casing; the two may differ — existing examples: folder
Venera-Next→ displayVeneraNext, folderSuwayomi-Tsumiru→ displayTsumiru, folderAirCard→ displayAirCard-iOS. Reuse the old folder name when the app was not renamed (repo migration only).If the new display name is ambiguous (repo name and README/source disagree, or the repo name is not a plausible app name), ask the user which name to use before moving anything.
-
Move the folder in git
git mv apps/<OldName> apps/<NewName>Use
git mv, nevermv+git add: it keeps rename detection and the trackedapps.jsoninside the folder moves with it. The rename is staged; do not commit (AGENTS.md).- Name unchanged? Skip the move and continue with step 3.
- Case-only rename on macOS (case-insensitive filesystem) needs two steps:
git mv apps/Foo apps/Foo-rename && git mv apps/Foo-rename apps/foo - If a screenshot or image file name contains the old app name, rename it
too and update its
[app] screenshotsentry.
-
Re-extract the project metadata from the new repo
Follow
/add-appsteps 2–3 against the new owner/repo, not the old URL (GitHub redirects old repo URLs, but the redirect carries stale asset names and owner data):- Find the project's own AltStore source and map it to the config fields (name, bundleIdentifier, developerName, subtitle, localizedDescription, iconURL, tintColor, screenshots, minOSVersion, downloadURL).
- No source: derive description/subtitle from the new README, and get
bundle_identifier/min_os_versionfrom a release ipa withtools/ipa_bundle_id.py. Never invent an identifier. - Re-verify the bundle identifier. A rename or ownership change can ship a different identifier; silently carrying the old one over makes the gallery install a different app (or fail). If the new repo offers no way to verify it, keep the existing value and say in the report that it was not re-verified.
- Re-check the version — always (the
/add-appstep 2f check, against the new repo). Download the latest release ipa (it is not optional here, even when a source supplied the metadata: the source's version is only what its own generator read) and compare the ipa'sCFBundleShortVersionString(tools/ipa_bundle_id.py --json) with the version altgen would derive from the new release tag minus a leadingv, or, whenversion_patternis set, the release name / asset filename. They can disagree (AltStore: tagv2.3, ipa2.3.1, build 63). Mismatch →version_source = "ipa"in[versions](step 4); agreement → the default, with noversion_source. Never fake a match by rewriting the tag,strip_v_prefix, orversion_pattern. Keep the ipa until the icon decision below is settled, then delete it. - Icon: prefer the new repo's artwork (repo, README,
assets/, or its AltStore source). If the new repo has none, keep the existingicon.png— it is still the app's artwork — rather than falling back to the small loose ipa icon; only usetools/ipa_icon.pywhen the existing icon is missing or obviously stale. - Screenshots: download the new repo's screenshots when it has them;
otherwise keep the existing local screenshots (still valid, already
hosted here). If there are none at all, warn at the end exactly as
/add-appstep 10 requires. - Copy assets into
apps/<NewName>/and reference them through thebebound/AltGalleryraw URLs — never pointconfig.tomlat the source's remote URLs.
-
Update
config.tomlRe-read the reference config (
apps/PiliPlus/config.toml) so the section order and shared wording stay current, then change the values in place — this is an existing app, do not rebuild the file from the template. Everything that names the app, the repo, or the folder changes:Location New value header comment new display name / one-line description [github] repo<new-owner>/<repo>[source] name,subtitle,descriptionnew display name; keep the shared "… AltStore source by AltGallery & AltGen" wording [source] websitenew repo URL [source] icon_url.../apps/<NewName>/icon.png[source] tint_colornew brand color when it changed (source tintColor, normalized to#RRGGBB)[app] namenew display name [app] bundle_identifierre-verified value (step 3) [app] developer_namenew owner (or the source's developerName)[app] subtitle,descriptionrefreshed from the new repo [app] icon_url,screenshots.../apps/<NewName>/...[app] tint_color,min_os_versionnew values when the icon / ipa changed [versions]re-derive from the new repo's latest release (step 5) [news] image_url.../apps/<NewName>/images/news.png[output] path = "apps.json"never changes.⚠️ The folder rename does not rewrite URLs — every
apps/<OldName>/segment in[source] icon_url,[app] icon_url,screenshots, and[news] image_urlmust be replaced by hand.⚠️ Release naming often differs in the new repo. Check the latest release's asset names and re-derive
[versions]:asset_patternis a regex, not a glob — the exact new ipa filename (e.g."^NewName\\.ipa$") or".*\\.ipa$"for any ipa;strip_v_prefix,version_pattern,build_version_pattern, andinclude_prereleasesonly when the new tags/assets need them — drop stale patterns that encode the old repo's scheme;version_source = "ipa"is re-decided, never inherited blindly: keep it only when step 3's check against the new repo still finds a tag/filename-vs-ipa mismatch; add it when the check now differs; drop it (and its comment) when the new repo's releases agree. Rewrite its comment with the new repo's values — an inherited one names the old tag;- update any comment that names old assets or tag formats.
-
Reset
news.tomlwhen the icon changed, otherwise update it in placeThe icon decides the palette:
render_news.pyderives the background from the icon's dominant color and the tints fromconfig.toml. Pinned[colors]overrides were chosen for the old icon, so once the artwork is new they cannot be trusted.-
Icon updated (step 3 replaced
icon.pngfrom the new repo or viatools/ipa_icon.py): deletenews.tomland write it fresh, exactly like/add-appstep 6 — copyapps/PiliPlus/news.toml(it documents every optional[colors]key as a comment), keep every[colors]key commented out so all colors auto-derive from the new icon, and change onlyname,tagline, and the header--out apps/<NewName>path:rm apps/<NewName>/news.tomlNever copy old pins into the fresh file — a pin sampled from the old icon is exactly the stale value this reset removes. Then re-render
images/news.png(step 6); the old promo image is invalid too. -
Icon unchanged: edit the existing file in place — header
--outpath,name→ new display name,tagline→ a short version of the new subtitle/description. Re-check a pinned color only when the brand/tint changed: keep it if it still fits, update it (and its inline comment) when it was derived from something stale, delete it when its reason no longer exists.
Either way, refresh the commented "current result" values in the
[colors]block so whoever edits the file next sees the palette actually in effect — a fresh copy of the template still carries PiliPlus's values.⚠️
render_news.pyneeds Pillow, which lives only in the project venv — always use.venv/bin/python3, never the barepython3. Remember the AGENTS.md caveat about sampling a dark/monochrome icon.Print the effective palette for the (new or unchanged) icon — run this block as-is; it stays at column 0 so the heredoc delimiter works:
-
PYTHONPATH=tools .venv/bin/python3 - <<'PY'
from pathlib import Path
from render_news import (load_configs, extract_icon_color,
derive_light_background, derive_background,
derive_bg_stops, auto_text_color,
derive_tagline_color)
out = Path("apps/<NewName>")
news, cfg = load_configs(out)
colors = news.get("colors", {}) or {}
app_tint = (cfg.get("app") or {}).get("tint_color")
src_tint = (cfg.get("source") or {}).get("tint_color")
tint = colors.get("tint") or app_tint or "#73B480"
tint_alt = colors.get("tint_alt") or src_tint or tint
bg = colors.get("background")
if bg is None:
icon_color = extract_icon_color(out / "icon.png")
bg = derive_light_background(icon_color) if icon_color else derive_background(tint)
mid, dark = derive_bg_stops(bg)
print("tint =", tint.upper())
print("tint_alt =", tint_alt.upper())
print("background =", bg.upper())
print("bg_mid =", (colors.get("bg_mid") or mid).upper())
print("bg_dark =", (colors.get("bg_dark") or dark).upper())
print("text_color =", (colors.get("text_color") or auto_text_color(bg)).upper())
print("tagline_color =", (colors.get("tagline_color") or derive_tagline_color(bg)).upper())
PY
-
Render the new promo image (AGENTS.md → Generating News Images):
./.venv/bin/python tools/render_news.py --out apps/<NewName>Always re-render: the icon and the name are baked into the image, so a refreshed icon or a reset
news.toml(step 5) must produce a newnews.png. Writesapps/<NewName>/images/news.pnginto the working tree — do not auto-commit it. -
Regenerate the source (never hand-edit
apps.json):./update.sh <NewName>This regenerates only this app's
apps.jsonand re-mergesall-apps.json. Both are gitignored and regenerated/committed by the CI workflow (.github/workflows/update.yml) — leave the modifiedall-apps.jsonin the working tree, never commit it. If altgen is rate-limited or the repo is private, useGITHUB_TOKEN=$(gh auth token) uvx altgen -c config.tomlin the folder. -
Update the README entry
Replace the app's
###heading and description line in Available Apps:### <a href="https://github.com/<new-owner>/<repo>"><img src="https://raw.githubusercontent.com/bebound/AltGallery/master/apps/<NewName>/icon.png" alt="<display name> icon" width="24" align="top"> <display name></a> One-line description.- Keep
align="top"on the icon (align="center"renders ~7px low). - The heading text is the display name, which may differ from the folder name — do not blindly copy the folder.
- Move the entry if the new display name changes its alphabetical position in the list.
- Refresh the one-line description when the project's description changed.
- Apply the same change to
README_CN.md— identical heading and icon URL, description line in Chinese. Both READMEs share the same app order.
- Keep
-
Sweep for stale references
grep -rn "<OldName>" --exclude-dir=.git --exclude=apps.json --exclude=all-apps.json . grep -rni "<oldname>" --exclude-dir=.git --exclude=apps.json --exclude=all-apps.json .Every remaining hit must be intentional (e.g. a comment that explains the old name). Check
git statusshows the folder as a rename (R), not a delete + untracked add. -
Report — summarize: old → new folder and display name, new repo, whether the bundle identifier changed (⚠️ a new identifier is a new app for AltStore — existing installs will not upgrade in place), tint/palette changes, icon source, screenshots refreshed or kept, any old-name references left on purpose, and whether screenshots are missing. Then stop — do not commit and do not ask about committing.
Checklist
- inputs confirmed; old folder matched case-sensitively
- new display name resolved from the new repo's source/README; ambiguity asked about
- folder moved with
git mv(two-step for case-only renames), history preserved - metadata re-extracted from the new repo, not the old redirect
- bundle identifier re-verified (or explicitly reported as unverified)
- tag/filename-derived version re-checked against the new repo's release ipa;
version_source = "ipa"added, kept, or dropped to match - icon refreshed from the new repo when available; existing icon kept when not
- new screenshots downloaded when available; existing kept otherwise; missing screenshots warned about
-
config.toml: repo, source/app names, developer, URLs, tint, min OS and[versions]patterns all updated; everyapps/<OldName>/URL gone -
news.toml: deleted and rewritten from the template when the icon changed (no old pins carried over); updated in place otherwise; name, tagline, header path and commented "current result" palette current -
images/news.pngre-rendered after the news.toml reset, with the new icon and name -
apps.jsonregenerated via./update.sh <NewName>, never hand-edited, not committed -
all-apps.jsonleft modified in the working tree, not committed - README entry updated in both
README.mdandREADME_CN.md(href, iconalign="top", display name, Chinese description) and re-sorted if needed - stale-reference grep clean or hits explained
版本历史
- 7e916c6 当前 2026-11-13 18:24


