---
description: "One-shot sync of the entire multi-agent ecosystem: Claude Code, Copilot CLI, pipeline repo, website, and the multi-agent-toolkit MCP server. Use when work is finished and should be propagated across Claude Code, Copilot CLI, the repos, the website and the multi-agent-toolkit server."
description-tr: "Tüm multi-agent ekosisteminin tek atımlık senkronu: Claude Code, Copilot CLI, pipeline repo, website ve multi-agent-toolkit MCP sunucusu."
argument-hint: "[release] [multi-agent-toolkit] [--platform=<macos|linux|windows>]"
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, WebFetch
---

# Multi-Agent Sync

**One command. Full ecosystem sync. No arguments needed.**

When invoked, it synchronizes all targets in order. It detects what changed, updates only the stale ones, and returns a result report. The host platform (macOS / Linux / Windows) is auto-detected; all dispatch commands stay platform-agnostic through `~/.claude/lib/credential-store.sh`.

**Input**: $ARGUMENTS (optional)

| Arg | Meaning |
|---|---|
| (none) | Full ecosystem sync: Claude Code, Copilot CLI, pipeline repo, website, multi-agent-toolkit MCP server. |
| `release` | Full sync + version bump + tag + npm publish + website deploy (includes the multi-agent-toolkit ship path) |
| `multi-agent-toolkit` | Run Step 3d only: gate, commit and publish the companion MCP server |
| `--platform=<macos\|linux\|windows>` | Override automatic platform detection. For CI / cross-platform smoke. |
| `"change description"` | Apply the description to every target at once |

## Targets

| # | Target | Path | Direction |
|---|-------|-----|-----|
| 1 | Claude Code (source of truth) | `~/.claude/commands/multi-agent/` + `~/.claude/multi-agent-refs/` + `~/.claude/agents/` + `~/.claude/scripts/` + `~/.claude/lib/` | source |
| 2 | Copilot CLI | `~/.copilot/copilot-instructions.md` + `~/.copilot/skills/` | <- from Claude |
| 2b | Codex CLI | `~/.codex/AGENTS.md` + `~/.codex/skills/multi-agent/` + `~/.codex/multi-agent-refs/` + `~/.codex/agents/*.toml` | <- from Claude (path-rewritten) |
| 3 | multi-agent-pipeline repo | `~/multi-agent-pipeline/pipeline/` | <- from Claude (genericized) |
| 4 | Website | `{owner}/{website-host}` | <- version + features |
| 5 | multi-agent-toolkit MCP server | resolved from `prefs.global.devToolkit` or the `mcpServers` registration | own repo: gate, commit, publish |

## Platform Support (macOS / Linux / Windows)

The sync skill must run on all three platforms. Commands go through the platform-agnostic shell layer; the following compatibility rules are preserved for every target:

| Platform | Detection | Implication |
|---|---|---|
| macOS | `[[ "$OSTYPE" == "darwin"* ]]` | Default. BSD `stat -f %m`, `security` for Keychain. |
| Linux | `[[ "$OSTYPE" == "linux"* ]]` | GNU `stat -c %Y`, `secret-tool` for Keychain (libsecret). |
| Windows (Git Bash / MSYS2) | `[[ "$OSTYPE" == "msys" \|\| "$OSTYPE" == "cygwin" ]]` | `~` resolves to `/c/Users/<name>`. Powershell `CredentialManager` for Keychain. POSIX paths inside Git Bash. |
| Windows (WSL) | `grep -qi microsoft /proc/version` | Linux toolchain inside WSL. Hostname format `WSL2-...`. |

**Cross-platform contract for synced shell scripts:**

1. Path handling: always quote (`"$f"`); use `$HOME`, never `/Users/...`.
2. Timestamps: try BSD `stat -f` first, fall back to GNU `stat -c`  -  both already covered by `~/.claude/lib/repo-cache.sh` pattern.
3. `find` traversal: include `-prune` for `node_modules`, `Pods`, `.build`, `DerivedData`, `.next`  -  same on every platform.
4. Sed / awk: prefer POSIX-portable invocations. Avoid GNU-only `-i` without `''` on macOS or `-r` (use `-E` instead).
5. Keychain I/O: always via `~/.claude/lib/credential-store.sh get|set`  -  never call `security` / `secret-tool` / `gcm` directly.
6. Verification: after Step 3 (REPO), run `bash -n` on every shell script in `pipeline/lib/` and `pipeline/scripts/` to surface portability regressions early.

## Default Behavior (no arguments)

Run every step automatically:

```
Step 1: PLATFORM    Detect macOS / Linux / Windows (Git Bash / WSL); export PLATFORM env
Step 1.5: DETECT    Compare timestamps, find stale targets
Step 2: COPILOT     Claude Code -> Copilot CLI (instructions + 53 sub-command skills)
Step 2b: CODEX      Claude Code -> Codex CLI (1 router skill + 51 specs as refs + 8 agent TOML)
Step 3: REPO        Claude Code -> pipeline repo (genericized, personal data scrub, bash -n on all sh)
Step 3c: PLUGINS    pipeline shared/external -> multi-agent-plugins marketplace (rebuild knowledge/,
                     bump changed plugins' patch version, commit + push the plugins repo)
Step 3d: DEV-TOOLKIT companion MCP server -> detect movement, run the ship gates, commit + publish
Step 4: WEBSITE     Version + phase/model counts -> {website-host} (i18n + projects.ts)
Step 5: Commit      Commit + push all changed repos
Step 6: Report      Summary: synced targets, platform, changed files, deploy status, multi-agent-toolkit version
```

If nothing is stale → report "All targets up to date" and stop.

---

## Sync rules

1. **Read first**  -  understand every target's current state before making any change
2. **Preserve file structure**  -  each target has its own format:
   - `commands/multi-agent/` SKILL tree + `multi-agent-refs/phases/`: detailed pipeline spec (frontmatter, phases, routing)
   - `copilot-instructions.md`: general development instructions + pipeline summary section
   - `multi-agent-pipeline/pipeline/`: generic open-source version (NO personal data)
3. **Sync the shared sections** (Claude ↔ Copilot):
   - Pipeline entries table (base / :local / :autopilot / :local-autopilot) + the Full-or-Short depth question
   - Project detection (URL-based + cwd-based)
   - Figma pipeline flow
   - Git conventions (author, branch, commit format)
   - Short-run model info (Opus)
   - Keychain management (registry, naming convention, multi-agent pattern)
4. **Do not duplicate**  -  summary in Copilot, full spec in Claude. Keep this distinction.
5. **Report**  -  show a short diff summary after sync


---

## Pipeline Repo Sync Rules (CRITICAL)

1. **Source**: `~/.claude/commands/multi-agent/` + `~/.claude/multi-agent-refs/` + `~/.claude/agents/` + `~/.claude/lib/`
2. **Target**: `~/multi-agent-pipeline/pipeline/`
3. **Genericization** (must be applied BEFORE writing to the target):
   - Jira project key (`prefs.global.defaultJiraKey`) -> `{JIRA_KEY}`, `PROJ` in examples
   - Personal names/emails -> generic placeholder
   - Branch names -> `develop`
   - Project names -> `my-app`
   - `{JIRA_HOST}`, `{BITBUCKET_HOST}`, `{CONFLUENCE_HOST}`, `{CORP_DOMAIN}` stay as-is
   - **Verify**: `grep` scan for personal/corporate data inside pipeline/ -> 0 results
4. **File mapping**:
   ```
   ~/.claude/commands/multi-agent/        -> pipeline/commands/multi-agent/
   ~/.claude/multi-agent-refs/            -> pipeline/multi-agent-refs/   (refs + pickers, non-command)
   ~/.claude/commands/sim-test.md         -> pipeline/commands/sim-test.md
   ~/.claude/agents/*.md                  -> pipeline/agents/
   ```
5. **Files NOT synced** (local-only, may contain personal data):
   - `~/.claude/multi-agent-preferences.json`
   - `~/.claude/CLAUDE.md`, `~/.claude/rules/`, `~/.claude/knowledge/`
   - `~/.claude/scripts/`  -  EXCEPT `pre-commit-check.sh`, `agent-guard.sh`, `agent-guard.py`, and `build-stack-plugins.mjs` (generic, synced)
   - `~/.claude/settings.json`
   - **Any `~/.claude/commands/multi-agent/*.md` whose frontmatter has `local-only: true`** - these are user/repo-specific alias wrappers that delegate to a private marketplace plugin (e.g. corporate `ai-ios-toolkit` skills exposed as `multi-agent:<name>`). Syncing them would leak the private plugin/skill names into the public pipeline. Filter before copy: skip every source file containing `local-only: true`, and after copy assert none reached `pipeline/commands/`.
     ```bash
     # backstop: no local-only wrapper may exist in the synced target
     grep -rl "^local-only: true" ~/multi-agent-pipeline/pipeline/commands/ 2>/dev/null \
       && { echo "ABORT: local-only wrapper leaked into pipeline/commands"; exit 1; } || true
     # backstop: no corporate marketplace/skill reference in the synced tree
     grep -rniE "ai-ios(-engineering)?-toolkit:(create-ui-component|evolve-ui-component|fix-bug|branch-and-pr|backlog|code-connect|figma-utility|resource-utility|component-wiki|component-docs|figma-setup)" \
       ~/multi-agent-pipeline/pipeline/commands/ 2>/dev/null \
       && { echo "ABORT: corporate reference leaked into pipeline/commands"; exit 1; } || true
     ```
   - **Localized picker descriptions are an installed-tree artifact.** When
     `outputLanguage=tr`, the installed `description:` values are Turkish and a
     `description-en:` sidecar holds the English original (see
     `localize-commands.mjs`). The repo must ALWAYS receive the English form:
     after copying commands into `pipeline/commands/`, normalize the copy and
     verify no sidecar or Turkish description leaked:
     ```bash
     # normalize: restore English descriptions, strip description-en sidecars
     node ~/multi-agent-pipeline/pipeline/scripts/localize-commands.mjs restore \
       --dir ~/multi-agent-pipeline/pipeline/commands/multi-agent
     # backstop: no sidecar may survive into the repo
     grep -rl "^description-en:" ~/multi-agent-pipeline/pipeline/commands/ 2>/dev/null \
       && { echo "ABORT: description-en sidecar leaked into pipeline/commands"; exit 1; } || true
     # backstop: repo description lines must be English (Turkish-diacritics proxy)
     grep -rn '^description:.*[çğıöşüÇĞİÖŞÜ]' ~/multi-agent-pipeline/pipeline/commands/ 2>/dev/null \
       && { echo "ABORT: localized description leaked into pipeline/commands"; exit 1; } || true
     ```

6. **Cross-platform smoke gate** (final step of the REPO sync, before push):
   ```bash
   # bash -n: parse-level syntax check on every shell script
   find ~/multi-agent-pipeline/pipeline/lib ~/multi-agent-pipeline/pipeline/scripts \
        -name "*.sh" -type f -exec bash -n {} \;
   # Should print nothing  -  any output is a portability regression.

   # Portability grep: flag known non-portable constructs introduced post-sync.
   grep -rnE "stat -f[^[:space:]]+[^|]|sed -i[^[:space:]'\"]" \
        ~/multi-agent-pipeline/pipeline/lib ~/multi-agent-pipeline/pipeline/scripts || true
   # Each hit needs an explicit GNU/BSD fallback per the Platform Support contract.
   ```
   Gate failure -> abort push, surface the file + line, fix in the `~/.claude/` source first, re-sync.

---

## Codex Sync (Step 2b)

Unlike the Copilot step, this one does **not** hand-copy files. The Codex tree is a
*transform* of the Claude tree, not a mirror of it, and the transform is real work:

- the 53 sub-command specs become reference files, because Codex silently truncates
  its skills block (see `cross-cli-contract.md` 2.6 for the measurement)
- every `$HOME/.claude/...` reference to a CLI-owned tree is retargeted, with
  `agents/<persona>.md` becoming `.toml` and the dispatcher becoming the router skill
- the 8 personas are regenerated as TOML with a model + reasoning-effort mapping
- shared state (`logs/`, prefs, `knowledge/`) is deliberately NOT retargeted

That logic lives in `install/codex.mjs` and is gate-locked by
`smoke-codex-install.sh`. Re-describing it here in prose would give the pipeline two
definitions of the same transform, and the prose copy would be the one that rots. So
the step runs the installer's Codex target:

```bash
cd "$HOME/multi-agent-pipeline"
node install.js --codex
```

**Verify** (the installer is quiet about correctness, only about counts):

```bash
# exactly one pipeline skill: more than one means the skills block will truncate
ls -1 "$HOME/.codex/skills" | grep -c '^multi-agent$'          # want 1
# every sub-command reachable as a ref
find "$HOME/.codex/multi-agent-refs/commands" -name SKILL.md | wc -l   # want the command count
# no reference left pointing at a tree a Codex-only install does not have
grep -rhoE '(\$HOME|~)/\.claude/(agents|scripts|lib|schemas|commands|multi-agent-refs|rules)' \
  "$HOME/.codex/skills" "$HOME/.codex/multi-agent-refs" | sort -u   # want empty
```

**MCP**: the installer runs `codex mcp add multi-agent-toolkit` itself. It is idempotent, and
it is skipped with a warning when `codex` is not on `PATH` -  never hand-edit
`~/.codex/config.toml`, which Codex owns (marketplace and plugin state live there
too).

**Order matters**: run this AFTER Step 3 (REPO), because the installer reads the repo
tree. Running it before means Codex gets the previous revision.

---

## Stack-Plugin Sync (Step 3c)

Stack skills are distributed as versioned plugins in the `mmerterden/multi-agent-plugins` marketplace. The pipeline's `pipeline/skills/shared/external/` is the **single authoring source**; the marketplace is a derived, versioned publish artifact. This step rebuilds it and publishes only when something changed.

**Source of truth:** author/vendor knowledge skills in `pipeline/skills/shared/external/` (the pipeline's own phases also consume them). The plugins' authored lifecycle skills (`index` / `reference` / `workflow` / `tools`) live in the plugins repo and are never touched by sync.

**Flow:**

```bash
PLUGINS_REPO="$HOME/multi-agent-plugins"
[ ! -d "$PLUGINS_REPO/.git" ] && gh repo clone mmerterden/multi-agent-plugins "$PLUGINS_REPO"
cd "$PLUGINS_REPO" && git pull origin main

# 1. Preview what would change (routing + version bumps), no writes:
node "$HOME/multi-agent-pipeline/pipeline/scripts/build-stack-plugins.mjs" --dry-run

# 2. Apply: rebuild each stack plugin's knowledge/ from shared/external,
#    regenerate plugin.json skills[], bump the patch version of any plugin
#    whose skill set changed. Cross-stack skills -> ai-common-toolkit;
#    Apple/Xcode-only skills -> iOS plugin only.
node "$HOME/multi-agent-pipeline/pipeline/scripts/build-stack-plugins.mjs"
```

**Version-based management:** the generator bumps a plugin's `version` (patch) only when its skill set actually changed. Idempotent - a no-op run bumps nothing. Consumers pick up new versions via `/multi-agent:update` (which runs `claude marketplace update multi-agent-plugins`).

**Help + README freshness (mandatory when the generator reported any change):**

1. Every plugin ships a `tools/help` skill that renders its catalog LIVE from `plugin.json`, so it needs no per-skill edits - but verify each changed plugin still registers it (`grep -c "./skills/tools/help" plugins/<name>/.claude-plugin/plugin.json` = 1) and that a new plugin gets one before publish.
2. Plugin README tables sync through `python3 tools/bump.py <plugin> --set <version>`; run `python3 tools/bump.py --check` and treat DRIFT as a blocker.
3. `python3 tools/validate.py` must be clean (it also enforces the CHANGELOG heading for every bumped version - write the entry, do not backdate).
4. When the pipeline's command surface changed in the same sync (new/renamed `/multi-agent:*` commands), confirm `commands/multi-agent/help/SKILL.md` and both pipeline READMEs (`README.md`, `README.tr.md`) were updated in Step 3 before pushing here.

**Publish (only if the generator reported bumps):**

```bash
cd "$PLUGINS_REPO"
if ! git diff --quiet; then
  # Identity: routed for {owner} (platformIdentityRouting). Never a literal: this file
  # installs everywhere and would misattribute a downstream user's commits.
  git config user.name  "{identity.name}"
  git config user.email "{identity.email}"
  git add -A
  git commit -m "chore: rebuild stack plugins from pipeline shared/external"
  gh auth switch --user {owner} 2>/dev/null || true
  git push origin main
fi
```

Never author skills directly in the plugins repo's `knowledge/` - they will be overwritten on the next rebuild. Author in `shared/external`, then run this step.

---

## Dev-Toolkit Sync (Step 3d)

The companion MCP server (`multi-agent-toolkit-mcp`) is its own repo with its own registry. Pipeline skills call its tools and several declare a minimum version, so when it moves it has to ship  -  otherwise the pipeline documents a tool no consumer has.

**Resolution**: identical to `/multi-agent:refactor` Step 0c  -  `prefs.global.devToolkit` first, then auto-detect from the `mcpServers` registration (`~/.claude.json`, `~/.claude/settings.json`), then skip. `enabled: false` skips.

**Detect movement**:

```bash
DT="<resolved localPath>"; cd "$DT"
git fetch origin --quiet 2>/dev/null || true
DIRTY=$(git status --porcelain)
AHEAD=$(git log --oneline @{u}..HEAD 2>/dev/null | wc -l | tr -d ' ')
VER=$(node -p "require('./package.json').version")
TAGGED=$(git tag --list "v$VER")
```

Moved = `DIRTY` non-empty, or `AHEAD` > 0, or no tag for `VER`. Nothing moved -> report `multi-agent-toolkit: up to date (v$VER)` and continue with the sync.

**Ship gates**  -  all must pass before commit or publish. A gate failure aborts THIS step only, reported with file + line; the rest of the sync continues.

**Prefer the repo's own gate script.** When the toolkit ships one (`npm run gates`, or `scripts/gates.sh`), run THAT and skip the inline list below - the repo owns the definition, this skill only owns the requirement. The inline gates are the fallback for a repo that has none, and the checklist the gate script itself must cover.

```bash
cd "$DT"
if node -e "process.exit(require('./package.json').scripts?.gates ? 0 : 1)" 2>/dev/null; then
  npm run gates || echo "ABORT: multi-agent-toolkit gates failed"
elif [ -f scripts/gates.sh ]; then
  bash scripts/gates.sh || echo "ABORT: multi-agent-toolkit gates failed"
else
  : # run the inline gates below
fi
```

1. **Syntax**: `node --check index.js` plus every `tools/**/*.js`.
2. **Boot + inventory**: handshake the server over stdio and count the registered tools.

```bash
TOOLS=$(printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"sync","version":"1"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
  | node index.js 2>/dev/null \
  | node -e 'let b="";process.stdin.on("data",d=>b+=d).on("end",()=>{const m=b.split("\n").filter(Boolean).map(l=>{try{return JSON.parse(l)}catch{return null}}).find(x=>x&&x.id===2&&x.result&&x.result.tools);console.log(m?m.result.tools.length:0)})')
[ "${TOOLS:-0}" -gt 0 ] || echo "ABORT: the toolkit served 0 tools"
```

3. **Advertised-count match**: `$TOOLS` must equal every advertised count (`package.json` `description`, the `README.md` header and its per-family rows). Fix the docs, never the gate  -  a stale count has shipped before.
4. **Packaging**: every directory the server loads at runtime must be inside `files[]`. A missing entry publishes a broken package (this has already caused one patch release).

```bash
PACK=$(npm pack --dry-run 2>&1)
for d in tools/*/; do printf '%s' "$PACK" | grep -q "$d" || echo "ABORT: $d missing from files[]"; done
```

5. **stdout hygiene**: `grep -rn "console\.log(" index.js tools/` must be empty  -  stdout carries the JSON-RPC frames, diagnostics belong on `console.error`.
6. **Personal-data scan**: no absolute `/Users/<name>` paths, no tokens, no corporate hostnames in the shipped files.
7. **Version contract**: every pipeline-side minimum (the `cross-cli-contract.md` tool table, `design-check`'s MCP requirement, `apple-archive-compliance`) must be satisfied by the version about to ship. A newly added tool that a pipeline skill now needs -> bump that declared minimum in the pipeline during this same sync.

**Version bump**: patch for fixes and docs, minor for a new tool, major for a removed or renamed tool (pipeline minimums depend on the surface).

**Ship**:

```bash
gh auth switch --user {owner}
git add -A
git commit -m "{type}(v$NEW): {what changed}"    # that repo's own commit convention
git tag "v$NEW" && git push origin main --tags
```

Publish to the registry declared in `publishConfig` through a throwaway userconfig. Never edit `~/.npmrc`, and never a bare `npm publish`  -  it lands on whichever registry the ambient config happens to name.

**The token depends on the registry AND on the package scope.** The registry picks
the credential *type*; the scope picks the *account*. Resolving on host alone
misroutes on any machine with more than one GitHub identity, which is the common
case for anyone with a work and a personal account.

| Registry | Credential type | Account chosen by |
|---|---|---|
| `npm.pkg.github.com` | GitHub PAT with `write:packages` (Classic; fine-grained PATs are not fully supported for Packages) | the package scope, e.g. `@{owner}` → the `{owner}` account's PAT |
| `registry.npmjs.org` | npm token (logical key `npm`) | the npm account that owns the scope |

Resolve the scope first, then the key:

```bash
SCOPE=$(node -p "(require('./package.json').name.match(/^@([^/]+)/)||[])[1] || ''")
# Prefer a scope-specific mapping; fall back to the generic key only when the
# machine has exactly one GitHub identity.
KEY=$(node -e '
  const fs=require("fs"),os=require("os"),p=os.homedir()+"/.claude/multi-agent-preferences.json";
  const km=(JSON.parse(fs.readFileSync(p,"utf8")).global||{}).keychainMapping||{};
  const scope=process.argv[1];
  process.stdout.write(km[`github_${scope}`] ? `github_${scope}` : "github");
' "$SCOPE")
```

**Pre-flight the scope, do not learn it from a 403.** GitHub tells you a token's
scopes on any authenticated request, so check before uploading rather than after:

```bash
scope_ok() {  # $1 = token; prints the login, non-zero when write:packages is absent
  local hdrs; hdrs=$(curl -sI -H "Authorization: token $1" https://api.github.com/user)
  printf '%s' "$hdrs" | grep -i '^x-oauth-scopes:' | grep -q 'write:packages'
}
```

**Candidate order for a `npm.pkg.github.com` publish.** The scope matters more than
where the token is stored, and the two are not correlated: on a machine with a work
and a personal identity, the hand-made PAT in the credential store may be the wrong
account or the right account without `write:packages`, while the `gh` CLI's own
OAuth token for that account often has it.

1. `github_<scope>` from `keychainMapping` (a PAT deliberately onboarded for this scope)
2. `gh auth token -u <scope>`  -  gh's stored OAuth token for that account
3. the generic `github` key  -  only when the machine has one GitHub identity

Take the first candidate that passes `scope_ok` AND whose `login` matches the
package scope. If none qualifies, stop before `npm publish` and report which
candidates were tried, what login each resolved to, and which scope was missing.
That report is the actionable output; a 403 body is not.

Measured on this machine, which is why the order is what it is:

| Candidate | login | scopes |
|---|---|---|
| `keychainMapping.github` | corporate EMU account | cannot publish to a personal scope under any grant |
| `mmerterden_Github_Auth_Token` | personal | `admin:public_key, gist, read:org, repo`  -  no `write:packages` |
| `gh auth token -u mmerterden` | personal | `gist, read:org, repo, workflow, write:packages` ✓ |

**Two 403s mean two different things, and neither says "wrong token" plainly:**

- `Unauthorized: As an Enterprise Managed User, you cannot access this content`  -
  the resolved token belongs to a corporate EMU account, which cannot publish to a
  personal scope at all. The mapping points at the wrong identity. Map the personal
  account's PAT under `github_<scope>` and re-run; do not "fix" this by granting
  the EMU token more scopes, because no scope makes an EMU account able to write
  to a personal namespace.
- `The token provided does not match expected scopes`  -  right account, missing
  permission. The PAT needs **Classic** with `write:packages` (plus `repo` for a
  private package). Regenerate it at
  `https://github.com/settings/tokens/new?scopes=write:packages,read:packages,repo`
  and re-onboard via `/multi-agent:setup`.

Report which of the two it was. "Permission denied" alone sends the user to
regenerate a token that was never the problem.

```bash
NPMRC=$(mktemp); trap 'rm -f "$NPMRC"' EXIT
REG=$(node -p "require('./package.json').publishConfig?.registry || 'https://registry.npmjs.org'")
HOST=${REG#https://}; HOST=${HOST%/}
case "$HOST" in npm.pkg.github.com*) KEY=github ;; *) KEY=npm ;; esac
TOKEN=$(bash "$HOME/.claude/lib/credential-store.sh" get "$KEY")
[ -n "$TOKEN" ] || echo "ABORT: no '$KEY' token - onboard it via /multi-agent:setup before publishing"
printf '%s\n' "registry=$REG" "//$HOST/:_authToken=$TOKEN" > "$NPMRC"
npm publish --userconfig "$NPMRC"
```

Verify the publish landed: on GitHub Packages `npm view <package> version --userconfig "$NPMRC"`; on npmjs read the registry directly (`curl -s https://registry.npmjs.org/<package>/latest`) rather than `npm view`, which can answer from a stale cache.

**Required release** (optional; ask first even in `release` mode, it halts every older install): `npm dist-tag add "$(node -p "require('./package.json').name")@$VER" required --userconfig "$NPMRC"`. Out of band, so promote or demote any time. Contract: `multi-agent-refs/rules.md` "Supported Version Gate".

**Approval**: outside autopilot and outside `release`, ask before shipping.

> "multi-agent-toolkit moved (N changed files, M unpushed commits, v$VER). What should I do?"

- Commit + push (no publish)
- Commit + push + publish v$NEW
- Skip this step

With `release`, or in autopilot, run the full ship path without asking. If the working copy holds band-E work from `/multi-agent:refactor` that was never approved, do not ship it silently  -  report it and ask.

**Consumer note**: a path-based stdio registration (`node <localPath>/index.js`) picks the change up on the next MCP restart with no reinstall; a packaged registration (`npx <package>`) needs the published version. State which one the resolved config uses in the Step 6 report.

---

## Website Sync (Step 4)

Propagate version, phase and model counts and feature descriptions to the website.

```bash
gh auth switch --user {owner}
WEBSITE_DIR="$HOME/{website-host}"
[ ! -d "$WEBSITE_DIR" ] && gh repo clone {owner}/{website-host} "$WEBSITE_DIR"
cd "$WEBSITE_DIR" && git pull origin main
```

| File | What to sync |
|-------|--------------|
| `src/lib/i18n.tsx` | Phase count, model count, feature descriptions (EN + TR) |
| `src/data/projects.ts` | Version number, tagline, description, feature list |

```bash
# A commit the platform does not recognise is pushed fine and never built, so the site
# keeps the old version. {identity} is the one routed to {owner}, not the run's own.
# Why, signature, recovery: `$HOME/.claude/multi-agent-refs/website-deploy.md`.
bash "$HOME/.claude/scripts/website-deploy-commit.sh" "{identity.name}" "{identity.email}" "{VERSION}" "$WEBSITE_DIR"
```

---

## Release Flow

When invoked with the `release` argument:

```
1. REPO          Claude Code -> pipeline repo (genericized)
2. VERIFY        Personal data scan -> 0 results; config-hygiene scan (scan-agent-config.sh) -> 0 HIGH
3. LINT + TEST   ESLint + smoke tests
4. VERSION       package.json version bump (patch/minor/major)
5. Commit + TAG  git commit + git tag v{VERSION}
6. PUSH          git push --tags -> release.yml auto-publish
7. DEV-TOOLKIT   Ship the companion MCP server if it moved (Step 3d gates, then publish)
8. WEBSITE       Version + features -> {website-host} (maintainer identity, build verified Ready)
9. COPILOT       Copilot CLI instructions + skills sync
9b. CODEX        Codex CLI router skill + refs + agent TOML (node install.js --codex)
10. Report       Summary: version, touched repos, deploy status
```

---

## Sub-Command Sync (Claude Code <-> Copilot CLI Skills)

> Codex takes the Step 2b path instead; see that section.

This runs on the Claude <-> Copilot axis. Codex is NOT synced here: it receives the
same 51 specs as reference files rather than as peer skills, via Step 2b -  see
`cross-cli-contract.md` 2.6 for why the parity axis differs per host.

| Claude Code | Copilot CLI |
|-------------|-------------|
| `~/.claude/commands/multi-agent/{cmd}/SKILL.md` | `~/.copilot/skills/multi-agent-{cmd}/SKILL.md` |

**53 commands are synced** (canonical inventory  -  must match `cross-cli-contract.md` section 1; drift = contract violation):

```
analysis, analysis-resolve, autopilot, build-optimize, channels,
complaint-analysis, create-jira, design-check, dev, dev-autopilot, dev-local,
dev-local-autopilot, diff-explain, feedback, forget, garbage-collect, help,
ios-coding-standard, issue, jira, kill, language, local, local-autopilot,
log, manual-test, prune-logs, prune-prompts, purge, refactor, resume,
resume-local, review, review-analysis, review-issue, review-jira, routines,
save, scan, search, setup, stack, status, store-ready, sync, test,
test-accessibility, test-dark-mode, test-dynamic-type, test-screenshots,
testflight-validation, uninstall, update
```

**NOT synced**: `$HOME/.claude/multi-agent-refs/*`  -  lazy-load references, Claude Code specific

**Claude -> Copilot**: add SKILL.md frontmatter (`name`, `description`, `user-invocable: true`, `argument-hint`)
**Copilot -> Claude**: simplify the frontmatter (`description` + `allowed-tools`), add `$ARGUMENTS`

---

## Keychain Token Access

| Operation | Token Source |
|-------|---------------|
| `gh` CLI (personal) | `{owner}` gh auth (Keychain) |
| `gh` CLI (work) | `{user}_<work-label>` gh auth (Keychain)  -  see `prefs.global.accounts[]` |
| npm publish | `NODE_AUTH_TOKEN`  -  `GITHUB_TOKEN` in CI, Keychain PAT locally |
| multi-agent-toolkit publish (Step 3d) | `npm` logical Keychain key -> throwaway `--userconfig`, registry from that repo's `publishConfig` |

```bash
gh auth switch --user {owner}  # for personal repos
```
