SuperAPI AI Directory

soia-meta-skill-release

General & other

View repository

Updated 10-16 08:39

About this skill

soia-meta-skill-release

After the skill PR has been merged, complete the local release wrap-up: install or update the changed skill, clean up old names, complete the Codex links, sync the consumer directories, and independently reconcile using the lock and version. Trigger words:"release skill"、"release X"、"skill release wrap-up"、「release skill」。

Customer-readable description

What this skill can do

Customer wants Skill will do Customer can see
Release one or more merged skills Install, update, symlink-sync, and verify lock/version Six-column release receipt
Rename or delete old skills Remove old installations and all managed directory remnants Cleaned count and zero-remnant verification

How customers use it

First confirm the target skill has been merged to the remote repository; this skill does not perform git, PR, merge, push, or publish remote state. Then provide the repository, skill list, and optional old names:

python3 skills/soia-meta-skill-release/scripts/release_skills.py \
  --repo <owner/name> \
  --skills <skill-a,skill-b> \
  --removed <legacy-skill> \
  --dry-run

After reviewing the dry-run, remove --dry-run to execute. By default it targets claude-code,codex, which can be overridden with --agents . Version verification resolves the local checkout in the following order:

  1. --repo-dir <repo-path> explicit path;
  2. the current process's SOIA_SKILL_REPOS_ROOT/<repo-name>;
  3. private YAML:--config → SOIA_META_SKILL_RELEASE_CONFIG_FILE → ~/.config/soia-skills/soia-meta-skill-release/config.yml in env.SOIA_SKILL_REPOS_ROOT;
  4. v1 private config directory read-only fallback (will output the suggested mv migration command to stderr);
  5. legacy maintainer local directory convention, used only as a deprecated backward-compatibility fallback.

Internally, the repository must still use the skills/<skill-name>/SKILL.md layout. For future new repositories, as long as --repo provides the corresponding any <owner>/<repo-name>, no script changes are needed.

Dependencies and installation

claude plugin marketplace add soia-team/soia-open-skills
claude plugin install soia-meta@soia

When you only want this one skill, you can use the npx route. Note that the skill will land in the shared source of truth ~/.agents/skills; if the plugin is also installed, the same skill will have two indexes and each will drift, so it is recommended to choose one of the two:

npx skills add soia-team/soia-open-skills -g -a '*' -s soia-meta-skill-release -y
Dependency Type Purpose What to do if missing
npx skills Hard dependency Install, remove, update, and maintain the lock Stop and report the failed step
soia-meta-sync-skills Optional dependency Only npx mode uses it in step 5 to sync the SOIA and WorkBuddy symlinks; the default plugin mode does not call it When using npx mode, install this skill first and retry;plugin mode is unaffected
Python 3 Hard dependency Execute the release script Install Python 3 and retry
PyYAML Optional dependency Read private config.yml Pass --repo-dir or use the current process environment variables

Private information and intermediate data

According to this repository's DATA_STORAGE_SPEC.md, this skill only reads and writes each AI skill installation directory and ~/.agents/.skill-lock.json; it optionally reads the private v2 config.ymlcontaining only the local checkout root. Copy Path configuration template before filling in; do not modify for this one setting .zprofile, nor read or save credentials, caches, or intermediate files. Terminal receipts only show skill name, version, link status, and failed step.

Logs and completion receipts

Stop at the first failed step, and output the steps reached plus the following six-column receipt:

Skill Action Repo version Installed version Symlinks (three places) Result
<skill> install/update/remove <version> <version> agents / claude / codex ok / removed / failed

Workflow

Delivery goes through the plugin marketplace,--install-mode defaults to plugin。By default it will not install any skills into ~/.agents/skills Install any skill—that would coexist with the plugin copy, causing the same skill to have two indexes that drift independently.

plugin mode (default)

  1. When --removed is present, clean up .agents、.claude、.soia、.workbuddy、.codex、.pi/agent six same-name remnants. Rename cleanup runs in both modes: leftover copies under the old name will override plugin updates and keep responding.
  2. Read the repo version and fill it into the receipt; record the installed version as -, record symlinks as plugin, record the result as published。
  3. Print the steps the user still needs to actually receive the changes: bump the version in both copies of plugin.json → the meta-repo regenerates the marketplace manifest and opens a PR for merge → client plugin update → plugin details verification.

ask mode (--install-mode ask, interactive selection)

Requires an interactive terminal. When running, ask the customer whether to install locally: entering y will perform an npx install to the target specified by --agents (the agents list can be overridden on the spot, e.g. claude-code,pi); entering n/Enter is equivalent to plugin mode, publish only without installing. In a non-interactive environment (an agent executing a script), using ask will error out and prompt you to switch to plugin/npx。

npx mode (--install-mode npx, explicit opt-in)

⚠️ Boundaries after the spec was finalized on 2026-08-06: the skill space of every agent must always usephysical installationand must be reproducible(source registered in the lock). Therefore: this mode only allows narrow per-skill commands npx skills add <repo> -g -a <agent> --copy -s <skill> -y(for multiple skills use repeated -s, a comma-separated list will be treated as a single name; qwen's id is qwen-code);Step 3's full npx skills update -g -y is disabled—it broadcasts a reinstall of all skills in the lock to all agent directories, equivalent to the disabled -a '*'; steps 4/5's symlink sync conflicts with the physical installation spec, so they are disabled as well. release_skills.py has not yet been reworked for this; do not use its batch steps until the rework is complete.

  1. Execute each item npx skills add <repo> -g -a <agents> --copy -s <skill> -y;--agents supports any npx skills -a agent id, such as claude-code、codex、pi(Pi installs into ~/.pi/agent/skills)、qwen-code。
  2. When --removed is present, run the same-argument npx skills remove, and clean up five remnants.
  3. Execute npx skills update -g -y(disabled, see the warning above; for updates, rerun step 1 for the target skill).
  4. Fill in symlinks for Codex(deprecated; Codex goes through the plugin marketplace).
  5. Call soia-meta-sync-skills to sync soia,workbuddy(deprecated; WorkBuddy goes through install_workbuddy_experts.py)。
  6. Reconcile ~/.agents/.skill-lock.json: all new skills must come from --repo, and old names must have zero remnants.
  7. Resolve the checkout in the order --repo-dir → process environment → private v2 config → read-only v1 config fallback → legacy compatibility directory, resolving checkout in that order, and comparing each item's SKILL.md version with the installed version.

Official release (dev branch model)

Precondition: must have the customer's explicit authorization for this instance. An official release is an outward-facing action—tag, Release, release PR, and marketplace pin refresh all change what external users receive. A customer asking to fix a bug or add a feature does not equalrequesting a release: merging the change into dev counts as delivery complete; report "already in dev, awaiting your decision on whether to release" and stop. When multiple AIs work in parallel, an uncoordinated release will send out others' unfinished work as well (this actually happened on 2026-08-03). Only --dry-run dry-run rehearsal requires no authorization.

The domain repo uses dual channels:dev handles daily merges (the version carries a -SNAPSHOT declaration of the next target, unchanged during the period, status identity uses the commit SHA);main always equals the latest official release. When the customer says **"official release X"**, execute:

python3 skills/soia-meta-skill-release/scripts/formal_release.py \
  --repo soia-team/<域仓> --repo-dir <本地路径> --summary "<一句话摘要>" --dry-run

after reviewing the dry-run plan, remove --dry-run and execute. The script completes five steps in order, stopping on the first failure:

  1. finalize PR → dev: each manifest (claude/codex/codebuddy independent tracks) strips -SNAPSHOT, and inserts the Release Notes before CHANGELOG.md—released means updated, same source as the GitHub Release, CHANGELOG follows the plugin cache, so users who installed the plugin can read it offline
  2. fast-forward push dev → main:git push origin <dev-sha>:refs/heads/main, main and dev point to the same commit
  3. v<X.Y.Z> tag is placed on that commit and pushed
  4. gh release create(title <插件名> v<X.Y.Z>)
  5. reopen train PR → dev: each manifest +patch enters -SNAPSHOT

then continue this skill's existing pin refresh and client update process (next section).

How to determine the version number

Reopen train defaults to +patch(1.11.0 → 1.11.1-SNAPSHOT)—right after a release, you don't yet know whether the next version is a bug fix or a skill addition; defaulting to +minor amounts to predicting "there will definitely be new features," and in practice it will be inflated: v1.11.0 actually only fixed one display defect, so semantically it should have been 1.10.1. Consistent with Maven release conventions.

Confirm the version by content before release: added a new skill/new capability → manually change dev to minor (e.g. 1.12.0-SNAPSHOT); breaking changes → major. After changing, run the release again; the script uses dev's version as authoritative.

The skill's own version must be bumped separately:plugin.json is the version of the plugin (delivery unit); each skills/<name>/SKILL.md 's frontmatter version is that skill's own version, and the two are independent. If you change a skill's body or script, you must bump that skill's version and updated_at— CI's check_skill_versions.py will block it (missed once on 2026-08-03: skill-release's script and body were changed, but the skill version stayed at 4.1.0).

Three non-reversible release constraints (all derived from incidents, do not change)

  1. dev→main uses fast-forward push, not PR merge. All three PR merge methods create commits on main that dev does not have—squash even breaks the ancestor relationship (the next release will definitely conflict; this actually happened with pkm/media on 2026-08-03), merge leaves a merge commit, rebase rewrites SHAs. Only fast-forward can make main and dev point to the same commit, so divergence is structurally impossible.
    • Premise 1:main must be an ancestor of dev . The script validates before release; if not satisfied, it aborts and requires first sync main→dev (this will not be satisfied if someone bypasses the process and directly modifies main, or if merge/squash was used historically).
    • Premise 2: dev HEAD's audit conclusion must be success. The script explicitly checks check-runs—— fast-forward is not skipping checks, and what is pushed is that commit which has already passed checks.
    • Repository settings:main 's enforce_admins must be disabled, otherwise protected branches reject direct pushes. Other protections (PR requirements,audit must pass) still apply as usual to ordinary changes.
  2. Between finalization and reopening the train is an invariant broken-window period: after step 1 removes dev's -SNAPSHOT , until before step 5 reopens, dev is in a violating state. Interruption in this interval will silently leave "dev stopped at the official version number." The script has an assertion as a fallback at the end, butmanual intervention or interruption must be self-checked。
  3. Release gate: meta-repo generate_marketplaces.py reads the manifest of the commit to be pinned, containing -SNAPSHOT directly refuses to generate the manifest——SNAPSHOT structurally cannot reach any client.

Supplementary notes from full-ecosystem batch release testing (2026-08-06, learned from releasing 10 repos all at once)

  1. When the meta-repo itself releases, the finalization PR must include derivative refresh in sync. Domain repos' main release first, meta-repo releases later, the meta-repo finalization PR's audit will successively hit marketplace freshness and skill-pages freshness (in testing, it hit CI twice in a row). Correct approach: on the finalization branch, additionally run generate_marketplaces.py、 generate_router_index.py、generate_skill_pages.py, and before push, pre-run all of audit's check steps locally until green, then push.
  2. Domain repos' default branch must be main. There was once a domain repo whose default branch pointed to dev, and codex marketplace add pulled the manifest according to the default branch, installing -SNAPSHOT. After gh repo edit <repo> --default-branch main correction and reinstall, it returns to the official version.
  3. Before WorkBuddy official installation, all domain repos' local checkout must be switched to main。 install_workbuddy_experts.py copies the checkout's current branch; after release, dev has already opened the next train (SNAPSHOT), and staying on dev will install SNAPSHOT as the expert version.
  4. Rebuild dev after release (when the customer requests "delete dev and re-pull from main"): open-source repos' dev protection forbids deletion and also forbids force-push; the process is GET protection config archive → PUT allow_force_pushes=true → force-push main+new train → immediately PUT to turn it back off → verify remote sha matches and protection is restored. Free version private repos have no classic protection API (GET returns 403), indicating there was no protection to begin with, so delete/push directly.
  5. Pitfall of manually pushing refspec in zsh:"$sha:refs/heads/main" 's :r will be swallowed by zsh as a modifier, producing a corrupted refspec; you must write "${sha}:refs/heads/main"。
  6. For repos containing feat, before release raise the train to next-minor(the rule already exists above in this section): in batch scenarios, first group by git log main..origin/dev counting each repo's feat commits, complete 6 repos' minor bump in one script, then run formal_release.pyrepo by repo, which is more stable than making ad hoc judgments per repo.

Health check: can be run anytime, must be run for inventory

python3 scripts/generate_marketplaces.py --help >/dev/null  # 元仓 checkout 内
python3 scripts/check_version_trains.py --repos-root <各仓父目录>

checks two things: ① version train invariants (dev has -SNAPSHOT, main does not) ② whether the next release can merge cleanly.When reporting ecosystem status, these two invariants must be verified; you cannot just copy the version number.——The two missed judgments on 2026-08-03 both stemmed from "only checking whether the values were correct, not verifying whether the rules held."

Trial-install dev (local verification snapshot version)

Trigger words:"Trial-install dev"、"Install dev version locally". The dev snapshot is only for local verification and must never be installed persistently.

  • Claude Code (recommended, session-level):claude --plugin-dir <域仓本地路径> to start a session; the current checkout (the SNAPSHOT version when in dev) is loaded as a plugin, and it is unloaded on exit without polluting the installed state; multiple --plugin-dircan be stacked. When only verifying without starting a session, use claude --plugin-dir <路径> plugin details <插件名>(--plugin-dir must come before the plugin subcommand).
  • WorkBuddy: after switching the local checkout to dev, run install_workbuddy_experts.py <插件名>——the script copies the local checkout, and the installed expert is the SNAPSHOT version, which can be directly distinguished by the version number in the UI.
  • Codex: there is no session-level mechanism;forbiddento persistently install dev/SNAPSHOT——SNAPSHOT will enter the client version comparison path, which is exactly the scenario the release gate intercepts on the marketplace side.

Plugin release and update process (after changes to the domain repository)

After skill changes are merged into the domain repository main, plugin users will not get them immediately——the sha pin in the marketplace manifest still points to the old commit. Perform the following steps to complete the release.

1. Confirm the domain repository changes have been merged

gh api repos/soia-team/<域仓>/commits/main --jq '.sha'

2. Regenerate the marketplace manifest in the meta-repository

The meta-repository main is branch-protected (must go through PR + audit must pass + enforce_admins), so the refresh can only be submitted as a PR and cannot be pushed directly. In the meta-repository checkout, run:

git checkout main && git pull && git checkout -b chore/refresh-marketplace
python3 scripts/generate_marketplaces.py && python3 scripts/generate_router_index.py

The two scripts re-fetch the latest sha of each domain repository main and rewrite .claude-plugin/marketplace.json、.agents/plugins/marketplace.json and the routing index. If git status has no changes, it means the manifest is already up to date; skip to step 5.

3. Submit the PR and merge

git add -A && git commit -m "chore(marketplace): refresh sha pins" && git push -u origin chore/refresh-marketplace
gh pr create --title "chore(marketplace): refresh sha pins" --body "刷新 sha pin 至各域仓最新提交。" --repo soia-team/soia-open-skills

Wait for audit checks to pass, then merge:

gh pr checks <PR号> --repo soia-team/soia-open-skills
gh pr merge <PR号> --squash --delete-branch --repo soia-team/soia-open-skills

audit The marketplace freshness check in will independently recompute the manifest once; if the two sides are inconsistent, it fails——this gate ensures that the published pin indeed points to the current main of the domain repository.

4. Verify the sha pin has been updated

gh api repos/soia-team/soia-open-skills/contents/.claude-plugin/marketplace.json --jq '.content' | base64 -d | python3 -c "import json,sys;print({p['name']:str(p.get('source',{}).get('sha',''))[:12] for p in json.load(sys.stdin)['plugins']})"

Compare with the domain repository sha from step 1; if they match, the manifest is up to date.

Do not use gh workflow run refresh-marketplace.yml: CI's GITHUB_TOKEN cannot push directly to the protected main, and the PR it creates will not trigger the audit check (GitHub suppresses it to prevent recursion); neither path works. Marketplace refresh is part of the release action and is explicitly completed by this process.

5. Guide clients to update

Claude Code:First record the installed manifest, and reconcile at the end——plugin update will fail directly for plugins that are not installed, and uninstall/reinstall-type operations can also easily miss installations:

claude plugin list | grep soia > /tmp/claude-soia-before.txt && cat /tmp/claude-soia-before.txt
claude plugin marketplace update soia
claude plugin update <域插件名>@soia

Reconcile at the end, and restore missing ones one by one with plugin install :

claude plugin list | grep soia | diff /tmp/claude-soia-before.txt -

After updating, Claude Code must be restarted for it to take effect. Users who have enabled autoUpdate will automatically complete these two steps on the next startup.

claude plugin details <名> For plugins from a private marketplace, the marketplace suffix must be included (<名>@<市场>); without it, it reports "not installed", which is easily misjudged as the plugin being lost. To check installation status, use plugin list is more reliable.

Codex:First record the current installed manifest——the cache will be deleted below, and deleting at the wrong granularity will also uninstall other plugins from the same marketplace:

codex plugin list | grep '@soia' > /tmp/soia-installed-before.txt && cat /tmp/soia-installed-before.txt

Only delete the marketplace staging(marketplace add will reuse the old clone; if you don't delete it, you won't be able to pull the newly added resource files):

rm -rf ~/.codex/.tmp/marketplaces/soia

The plugin cache only deletes the target one,soia is the marketplace name, not the plugin name,rm -rf ~/.codex/plugins/cache/soia it will remove all8 pluginsunder that marketplace at once:

rm -rf ~/.codex/plugins/cache/soia/<域插件名>
codex plugin marketplace add soia-team/soia-open-skills
codex plugin add <域插件名>@soia

Finally, compare against the installation checklist, to confirm there is no collateral loss; if anything is missing, add them back one by one with plugin add :

codex plugin list | grep '@soia' | diff /tmp/soia-installed-before.txt -

Codex has no automatic update mechanism and must be done manually. Skipping the step of deleting the staging area will result in "the command reports success, but the content is still the old version" — this was actually encountered on 2026-07-27: the corp marketplace staging was stuck on an old version without assets/icon.svg ,composerIcon pointed to a nonexistent file, the interface fell back to a generic icon, and during troubleshooting it was misjudged as an incorrectly written path.

6. WorkBuddy experts (only do this when the customer is using WorkBuddy)

WorkBuddy is an Electron desktop app,with no CLI— there is no workbuddy plugin install, nor is there a marketplace channel that can point to our GitHub. So this step is done by a script; do not look for an equivalent command:

python3 skills/soia-meta-skill-release/scripts/install_workbuddy_experts.py --dry-run

After confirming the plan, execute (with no arguments it installs all; you can also provide only the plugin names to install):

python3 skills/soia-meta-skill-release/scripts/install_workbuddy_experts.py

The script copies the domain repo checkout into my-experts/plugins/<插件名>, then calls the official WorkBuddy register_expert.py registration. Three empirically tested constraints dictate that this is the only way:

Constraint Empirical conclusion
Directory Self-built experts only recognize the hardcoded my-experts, which appears in 38 places in the app; if placed elsewhere, they do not show up
Symlink does not work. The official validate_expert.py resolves the path resolve(), and after following it, it determines that it is "not under the expert directory"
Remote Marketplace entry source can only be a path string, with no sha pin layer;expert/install deep links require sharecode, going through the official cloud

After installationthe customer must be asked to restart WorkBuddy, otherwise the new expert will not appear under [Experts · Skills · Connectors → My Experts].

Verification: after summoning that expert, ask "how many usable skills do you have"; all skills from that domain should be present; when not summoned, they are not present.

7. Reclaim old version caches

Both clients, after plugin update only add new version directories,and do not reclaim the old ones; Claude's .in_use marker is also unreliable (in practice, both the old and new versions of the same plugin carry this file). If not cleaned up, they will accumulate linearly and interfere with troubleshooting — using find to find resources will match multiple version directories, andls will produce absurd results when counting skills.

python3 skills/soia-meta-skill-release/scripts/prune_plugin_cache.py

After confirming the dry run is correct, execute:

python3 skills/soia-meta-skill-release/scripts/prune_plugin_cache.py --apply

Keep the highest value by semantic version and delete the rest; directories with non-semantic versions (such as the official plugin's latest) are skipped entirely. The cache can be re-pulled from the marketplace at any time, so even deleting the wrong one just means downloading it one more time.

8. Verification

claude plugin list
codex plugin list

Confirm that the target plugin version has changed and the status is enabled.

Domain repo and plugin comparison

Domain repo Plugin name
soia-open-dev-skills soia-dev
soia-open-dev-design-skills soia-dev-design
soia-open-pkm-vault-skills soia-pkm-vault
soia-open-media-content-skills soia-media-content
soia-open-cwork-office-skills soia-cwork-office
soia-open-edu-course-skills soia-edu-course
soia-open-env-skills soia-env
soia-open-skills soia-meta

Boundaries and verification

  • Only perform post-merge local cleanup; pre-release merge is done by the caller.
  • --dry-run does not execute any commands or file writes, and only outputs a plan receipt.
  • Forward tests should mock in a temporary HOME subprocess, covering command order, fail-fast, cleanup of the five old names, Codex supplementary links, the lock branch, and dry-run.

Release prerequisite: cross-repo installation section checkup

The audit_skills.py in each domain repo differ from one another,private-skills and some don't even have that script, so the rule "the installation section covers three first-class hosts"cannot be uniformly verified in each repo's CI. The portal repo's CI only uses --self to check its own few skills.

Before release, run the full batch once locally (requires working copies of each domain repo under the same parent directory):

python3 <soia-open-skills>/scripts/check_install_sections.py --repos-root <各仓的父目录>

It scans <repo>/skills/* and <repo>/*/skills/*(covering private-skills' workspace/ and harness/ subrepo directories); a non-zero exit means some skill is missing a first-class host.