---
name: multi-agent-setup
description: "First-run setup wizard: keychain token discovery, Git Identity onboarding, and pipeline preparation. Use when the pipeline is being set up for the first time, or tokens and git identity need onboarding."
argument-hint: "[jira-keys | hosts | accounts]  -  optional; no argument runs the whole onboarding"
user-invocable: true
metadata:
  language: en
---

`$HOME/.claude/multi-agent-refs/` ships with Claude Code.

## Gotchas

- A token is read and written only through `~/.copilot/lib/credential-store.sh`. Never echo it, never pass it on argv, never paste it back to the user.
- A mapping in `keychainMapping` with no value in the store is not onboarded: offer the Token Save Flow, do not ask for the secret in chat.
- Hosts are stored without a scheme (`jira.example.com`); the callers add `https://`.
- Publishing tokens differ by registry: npmjs takes an npm token, GitHub Packages a classic PAT with `write:packages` from the account that owns the scope.

## Step 0 - What is actually wrong

```bash
node "$HOME/.copilot/scripts/doctor.mjs"
```

Run it before the first question and again at the end. The first run makes setup targeted: never ask for a token that is already mapped and answering. The second run is how setup ends - "setup complete" is a claim, and the doctor's exit code is the evidence. Report both in `outputLanguage`; if the second run still shows a BLOCK, say so plainly rather than closing on the word "complete".

## Setup (Credential Store + Git Identity Onboarding)

Uses `~/.copilot/lib/credential-store.sh` to read/write secrets in the macOS Keychain (`security`, built in); the pipeline is macOS only (ADR-0012). Run `~/.copilot/lib/credential-store.sh doctor` first  -  it prints the platform and whether the backend is usable. The shell driver auto-delegates `get`/`set`/`delete` to the deterministic Python helper at `$HOME/.copilot/scripts/keychain.py` (Python 3 stdlib only, no external deps). The Python path writes both `-l` (label) and `-s` (service) attributes so items are findable regardless of how callers look them up. Set `KEYCHAIN_DELEGATE=0` to force the raw shell path.

### Step 0  -  Language Selection

Ask BEFORE anything else, so every subsequent setup prompt and the rest of the pipeline honour the choice. The pipeline has two language axes:

- `promptLanguage`: picker chrome only (the `AskUserQuestion` `header` chip, host error UI, internal contract identifiers). **Not configurable  -  fixed to `en`**; the chip is capped at 12 chars, which most Turkish equivalents overflow.
- `outputLanguage`: the assistant's non-interactive explanations, status updates, error messages and pipeline-generated reports. Set here, changeable later via `/multi-agent-language <en|tr>`.

`promptLanguage` is seeded as `"en"` and never offered to the user. **External payloads always stay English** (commits, PR titles/bodies, Jira comments, wiki content, reviewer/triage prompts, agent-log.md).

The wizard asks one question (English UI per skill-prompt rules):

```
Which language should the assistant use for ITS OWN explanations,
status updates, and pipeline-generated reports?
  1) English
  2) Türkçe
Select (1-2):
```

**Rules:**

- **Already set in prefs** → skip silently. User can change later with `/multi-agent-language <en|tr>` or `/multi-agent-language output <en|tr>`.
- **New value** → validate against the schema enum (`en`, `tr`), write `prefs.global.outputLanguage` via atomic rename, force `prefs.global.promptLanguage = "en"`, then echo a confirmation in the just-chosen `outputLanguage`:
  - `en`: `outputLanguage=<Y>. promptLanguage stays "en". External payloads (PR bodies, Jira comments, reviewer prompts) stay English regardless.`
  - `tr`: `outputLanguage=<Y>. promptLanguage "en" sabit. Dış çıktılar (PR açıklamaları, Jira yorumları, review istemleri) her durumda İngilizce kalır.`
- **Unknown input** → re-ask once, then abort setup with a hint pointing to `/multi-agent-language`.

From here on, every prompt in Steps 1-6 follows the per-field matrix in
`$HOME/.claude/multi-agent-refs/rules.md` ("Language Application"): `question`, `label`
and `description` render in `outputLanguage`, `header` stays English; wizard status and
the post-setup summary follow `outputLanguage`. `promptLanguage` governs only the button
and chip chrome, never the question a user reads.

### Step 0.5  -  Dependency check

```bash
~/.copilot/lib/credential-store.sh doctor
command -v pdftotext >/dev/null 2>&1 || echo "pdftotext: MISSING"
```

| Output | Action |
|--------|--------|
| `Backend: ✓` | Continue to Step 1 |
| Platform `unknown` or `ERR: macOS only` | Halt  -  the pipeline runs on macOS only (ADR-0012). |
| `pdftotext: MISSING` | Advisory. Print `brew install poppler`, continue. `.docx` needs nothing. |

### Credential Key Naming Rule

- **New token** (first-time creation in Step 3): ALWAYS use the standard key name (`${USER}_Jira_Access_Token`, etc.). This ensures cross-machine consistency.
- **Existing token** (discovered in Step 1): NEVER rename or migrate. Map the discovered key name as-is into `keychainMapping`. The mapping layer abstracts the actual key name  -  the pipeline never reads the credential store directly by standard name.

### Step 1  -  Dynamic Credential Discovery

Full procedure: `$HOME/.claude/multi-agent-refs/setup/discovery.md`. Read it for Step 1.

### Step 2  -  Show Discovery Summary + Save Mapping

Show results grouped by status:

```
Keychain Token Discovery (${USER}):

  Jira               -> ${USER}_Jira_Access_Token
  GitHub             -> ${USER}_Github
  Figma              -> ${USER}_Figma_Access_Token
  Firebase           -> ${USER}_Firebase_Access_Json
  Confluence         -> MISSING
  Jenkins            -> MISSING
  ---
  Git identities: check preferences file (see Step 3)

  Mapped: 8 | Missing: 4
```

Save the resolved mapping to `prefs.global.keychainMapping`  -  every service ID gets an entry, the discovered key name verbatim or `null` when nothing was found:

```json
{ "jira": "${USER}_Jira_Access_Token", "github": "${USER}_Github", "confluence": null, "...": "..." }
```

Full key list and shape: `$HOME/.claude/multi-agent-refs/keychain.md`.

`null` = not mapped (missing or skipped). Pipeline phases read this mapping to retrieve tokens dynamically  -  never hardcoded key names.

### Step 2.7  -  Operational reporting

Ask (in `outputLanguage`), and proceed only on an explicit yes:

```
Do you have an operational-reporting token from your admin?
[ Paste token / Skip ]
```

On paste, store it in the credential store ONLY - never in a file, prefs value,
git or any synced tree - under the standard per-user name, so it is revocable on
its own:

```bash
pbpaste | ~/.copilot/lib/credential-store.sh set "${USER}_Usage_Ingest_Token" -
pbcopy < /dev/null
```

Then, **whether they pasted or skipped**, run:

```bash
node "$HOME/.copilot/scripts/usage-register.mjs" --json
```

If its JSON line says `"needsPersonalAccount":true`, offer once: `gh auth login`,
or an SSH key on the personal account (the "Personal account" step in
`$HOME/.claude/multi-agent-refs/features/usage-reporting.md`).

It maps whatever token exists and switches reporting on, and requests a
per-machine write-only one when none resolves. Setup is a registration entry point
in its own right, so a machine that runs setup and never runs update still reports.
What is sent, what never is, and `usageLog.optOut`:
`$HOME/.claude/multi-agent-refs/features/usage-reporting.md`.

### Auto-learned fields (no setup step needed)

**Jira Project Key**: auto-learned from pipeline usage. User runs `/multi-agent ABC-123` → `ABC` is extracted and saved to `prefs.global.defaultJiraKey` (first ever) and `prefs.projects[{project}].jiraProjectKeys` (per-project). Multiple keys accumulate over time  -  most recent first, deduped. Phase 0 Step 1 (Parse Input) handles extraction.

**Git Identity**: auto-learned from token onboarding. Each token saved in Step 3 triggers an identity prompt (see Token Save Flow Step C). No separate identity setup step.

### Step 3  -  Interactive token onboarding (for missing tokens)

Git identities are NOT asked as a separate step. They are learned when tokens are added via the Token Save Flow below. Each token save triggers an identity prompt:

```
Token saved. Which git identity uses this token?
  1. Ada Lovelace <ada@example.com>
  2. Ada Lovelace <ada@personal.com>
  n. Create new identity
Select:
```

- First token ever → no existing identities → asks name + email → creates identity → maps token
- Subsequent tokens → pick existing or create new → maps token to that identity's `servicePatMap`
- Multiple accounts per platform supported (e.g. two GitHub tokens, each mapped to a different identity)
- Identity is stored in `prefs.global.identities[]`, token-to-identity binding in `identity.servicePatMap`

**Legacy compatibility**: If `prefs.global.gitIdentities` (deprecated) is present, `migrate-prefs.mjs` renames it to `identities` automatically.

For each MISSING service, run the **Token Save Flow** below. When creating new tokens, use the **standard key name** from Step 1b table.

#### Sequential onboarding loop (strict)

The Step 2 discovery summary is never the end of setup. Immediately after showing it, walk the missing services ONE BY ONE in this fixed order and run the Token Save Flow for each:

1. `jira`
2. `bitbucket_token` (then `bitbucket_user` right after, if missing)
3. `github`
4. `confluence`
5. `figma`  -  Figma Personal Access Token (Tier 2, REST)
6. `figma_mcp`  -  Figma MCP auth (Tier 1); its own prompt, never folded into the PAT prompt
7. `fortify`
8. `graylog`
9. `firebase`
10. `jenkins`
11. App Store Connect block (Step 3b; only when the target repo is iOS)
12. Google Play block (Step 3d; only when the target repo is Android)

Loop rules:

- **One prompt per service.** Never collapse the list into a single "add missing tokens now?" yes/no, and never offer one multi-select covering all services: each service has its own source URL, key name, identity binding and host prompt, so each gets its own Token Save Flow pass.
- **Each service pass asks, in order: token → author → host.** Step A/B collect the secret, Step C binds the git identity (author)  -  Bitbucket and GitHub included; every saved token gets an author  -  and Step 3.5 asks the host domain inline for hosted services (jira, confluence, bitbucket, fortify, graylog) - plus the Fortify project versions and the optional Graylog test instance. GitHub and Figma skip the host question (fixed domains); `bitbucket_user` skips author binding (username, not a PAT). Never end a service pass with the token saved but the author or a required host unasked.
- **Skip is per-service.** A Skip answer leaves that service's `keychainMapping` at `null` and the loop continues with the next service. Skips never abort the loop.
- **`figma` and `figma_mcp` are distinct credentials for distinct tiers  -  ask both.** Skipping the PAT does not imply skipping MCP, and vice versa. The `figma_mcp` pass opens with a Remote / Local mode question instead of a clipboard paste (see "Figma MCP Setup" below): Remote generates a `figu_` OAuth token for the current user, Local wires the PAT-based local server.
- The loop ends only when every missing service has been asked exactly once; then continue to Step 4 (Verify all).

---

### Token Save Flow (reusable)

Full procedure: `$HOME/.claude/multi-agent-refs/setup/token-save-flow.md`. Read it for every token prompt (Steps 3, 3b, 3c and any command that saves a token).

### Step 3b  -  App Store Connect onboarding (iOS only, optional)

Runs inside Step 3. Every part of the API key lives in the Keychain, the `.p8` included: ask for the `.p8` as a path, validate it BEFORE saving with `node "$HOME/.copilot/scripts/probe-store-key.mjs" --asc ...` (exit 0 only), map `appstore_connect_private_key`, store it from the file with `credential-store.sh set appstore_connect_private_key - < <AuthKey_<keyId>.p8>`, run `node "$HOME/.copilot/scripts/write-store-config.mjs"`, then offer (picker, default keep) to delete the file. `store_asc_*` and `ios_testflight_validate` then read the key from the Keychain. The five entries, the tier picker and the probes: `$HOME/.claude/multi-agent-refs/setup/app-store-connect.md`. On Skip, nothing is written.

### Step 3c  -  Firebase / Crashlytics onboarding (optional)

Runs inside Step 3. Measure first  -  `bash "$HOME/.copilot/lib/fetch-crashlytics.sh" --probe` answers `tier-1-ready` / `tier-1-no-grant` / `malformed` / `unreachable`  -  then ask by that answer, never blind. Store the Firebase service-account JSON as issued; never base64 it. Skip writes nothing. Then offer appId discovery: `bash "$HOME/.copilot/scripts/firebase-app-discovery.sh" <repo> --json` reads the pairs the repo already carries, for `firebase.accounts[].apps[]`  -  it saves a round trip per run; skipping costs nothing. The picker, both tiers, the role-request wording and the v1alpha caveat: `$HOME/.claude/multi-agent-refs/setup/firebase.md`.

### Step 3d  -  Google Play onboarding (Android only, optional)

Runs inside Step 3, only when the target repo is Android. One entry, `google_play`: the service-account JSON the toolkit's `store_play_*` tools use, which lets `/multi-agent-store-ready` run Android Gate 2. Ask for the JSON's path, validate BEFORE saving with `node "$HOME/.copilot/scripts/probe-store-key.mjs" --play --service-account <path>`, map `google_play`, then `credential-store.sh set google_play - < <path>` and `write-store-config.mjs`. Account prerequisites, the picker and the probe hints: `$HOME/.claude/multi-agent-refs/setup/google-play.md`. Skip writes nothing.

### Step 3.5  -  Host Prompt (embedded in Token Save Flow)

Full procedure: `$HOME/.claude/multi-agent-refs/setup/token-save-flow.md`. Read it for the host question inside the Token Save Flow.

### Step 3.6  -  Per-account host overrides (optional)

**Runs only when** `~/.copilot/lib/account-resolver.sh` returns **more than one** account AND at least one account has Jira or Bitbucket tokens.

Use case: one user with two identities (e.g. agency + client), each with its own Jira host; `prefs.global.hosts.jira` holds only one default.

For each non-default account, ask once: "Account {label} has Jira/Bitbucket tokens but no host override yet. Use the global default ({hosts.jira}) or set a per-account host?" with options **1) Use global default** / **2) Set per-account host**. On `2`, prompt for `jiraHost` / `bitbucketHost` / `confluenceHost` (each optional) and save to `prefs.global.accounts[]`, e.g. `{ "id": "client-x", "jiraHost": "jira.client-x.example.com", "bitbucketHost": "bitbucket.client-x.example.com" }`.

`account-resolver.sh` reads this on every invocation; per-account override wins over `global.hosts.*`. Skip this step on autopilot  -  `accounts[]` stays empty until the user opts in via `/multi-agent-setup accounts`, which opens this editor without re-running token discovery.

---

### Step 4  -  Verify all

Re-run scan from Step 1. Show final status:
`All tokens present. Pipeline ready to use.` Then list the optional per-project features (configured on first use, nothing to do now): **Phase 5 Report Step 2 Wiki** (component wiki pages + Figma screenshots; activates when the task is a component AND a Figma token is in Keychain; adapters submodule / in-repo / github-wiki / separate-repo; first run asks: auto-detected path, custom path, skip once, or disable for this project, persisted); **channels** (post-hoc multi-channel reporter for Jira/Confluence/Wiki/PR, any branch with an open PR); **Default reviewers** (auto-fetched from the Bitbucket default-reviewers endpoint or GitHub CODEOWNERS on first PR). Next step: `/multi-agent "PROJ-12345"` to start a first task, `/multi-agent-help` for the full usage guide.

If still missing, show which ones and let user skip (pipeline will re-ask for any needed token at Phase 0).

### Step 5  -  Repo Discovery (optional)

Pre-populates the Phase 0 project picker with repos the user is active in, merging three independent sources: local `$HOME` scan (depth 3, repo markers), Bitbucket (last 90 days, token-gated), GitHub (owner + collaborator, push in last 90 days, token-gated). A failing source skips only itself and is reported (never swallowed); discovery is idempotent and never destructive.

```
Step 5  -  Repo Discovery (optional)
Scan for recent repos now to pre-populate the Phase 0 picker?

  y  -  all three (local + Bitbucket + GitHub, ~10-30s)
  l  -  local scan only (fastest, no network)
  n  -  skip (Phase 0 will prompt for repo path manually)
```

The scan commands, merge-by-origin dedup, the multi-select picker, the `recentProjects` / `serviceStatus` prefs shapes and the skip / degrade rules live in `$HOME/.claude/multi-agent-refs/setup/repo-discovery.md`  -  read that file when the user opts in.

### Step 6  -  Remember as Multi-Repo Group (optional)

After Step 5, if the user picked **2 or more** repos, offer to remember the combination as a named group; Phase 0's project picker surfaces saved groups at the top, so a multi-repo task becomes a single keypress next time. Auto-skipped if Step 5 was skipped or fewer than 2 repos were selected (single-repo selection is a no-op).

```
Step 6  -  Remember as a multi-repo group (optional)
You picked 2 repos: Checkout, UIComponents.
Save this combo for one-tap selection in Phase 0?
  Group name (Enter for auto): [Checkout + UIComponents]
  Skip with 'n'.
```

- **Auto-name** (suggested, editable): 2 repos `"<repoA> + <repoB>"`; 3-4 repos `"<repoA>, <repoB>, <repoC>(+1)"`; 5+ repos `"<repoA> + <count-1> more"`. Labels use the human label from `recentProjects` (path basename, not the full path).
- **Save** to schema `global.recentGroups[]` (`repos` required, `label` / `count` / `lastUsed` optional), e.g. `{ "label": "Checkout + UIComponents", "repos": ["~/dev/Checkout", "~/dev/UIComponents"], "count": 1, "lastUsed": "2026-04-15T21:08:00Z" }`.
- **LRU + dedup**: a new group is unshifted to the front. The same set of `repos` (order-independent compare) bumps the existing entry to the front, increments `count`, updates `lastUsed`; do NOT create a duplicate. Schema cap is 10 entries; the oldest past the cap are dropped.
- **`n`** → not saved; the picker still works for one-time use, and a future Phase 0 selection of the same combo re-prompts.

### Step 7  -  Platform Identity Routing (optional)

Full procedure: `$HOME/.claude/multi-agent-refs/setup/identity-routing.md`. Read it for Step 7.

### Cross-Platform Notes (Step 1-9) and Token -> Source Mapping

Every read and write goes through `~/.copilot/lib/credential-store.sh`, which wraps the macOS Keychain; stay on the helper rather than calling `security` directly. Clipboard handling and the no-backend case: `$HOME/.claude/multi-agent-refs/keychain.md` "Credential store mechanics". Standard key names are a convention for NEW tokens; the key names, token types and where to generate each one live in `$HOME/.claude/multi-agent-refs/keychain.md` "Standard key names". Git identities are stored in preferences, never in the credential store.

### Inline Token Save Pattern

All tokens go through `~/.copilot/lib/credential-store.sh` (auto-delegates to `~/.copilot/scripts/keychain.py`, see Setup above).

```bash
# PAT / API Key / JSON contents (from clipboard)
pbpaste | ~/.copilot/lib/credential-store.sh set "<SERVICE_NAME>" -
pbcopy < /dev/null  # clear clipboard

# Non-secret value typed inline (a secret on argv is visible to ps and lands in shell history)
~/.copilot/lib/credential-store.sh set "<SERVICE_NAME>" "<VALUE>"

# Key file from disk (the .p8, the Google Play JSON; or when the clipboard is unavailable)
~/.copilot/lib/credential-store.sh set "<SERVICE_NAME>" - < /path/to/file.json

# Read  -  a JSON value comes back exactly as it was stored, newlines included
~/.copilot/lib/credential-store.sh get "<SERVICE_NAME>"

# Maximum stdin hygiene (the secret never lands on argv / shell history): the Python helper with the - sentinel
pbpaste | ~/.copilot/scripts/keychain.py set "<SERVICE_NAME>" -
```

### Figma MCP Setup (Remote / Local question)

Full procedure: `$HOME/.claude/multi-agent-refs/setup/figma.md`. Read it for the Figma MCP question.

### Security Rules

- Never paste tokens into terminal or chat  -  use `pbpaste` for input
- Never hardcode tokens in source code
- Never commit tokens to git history
- Clear clipboard immediately: `pbcopy < /dev/null`
- `2>/dev/null` suppresses stderr (prevents metadata leakage)

### Setup from Scratch Summary

On a new machine: (1) `npx @{npm-scope}/multi-agent-pipeline install`; (2) `/multi-agent-setup`; (3) the interactive flow: token scan (shows what is missing) -> Git Identity creation (name + email) -> inline commands for missing tokens -> final verification; (4) ready: `/multi-agent "PROJ-12345"`, `/multi-agent "https://github.com/.../issues/42"` or `/multi-agent "bug description"`.

All tokens are optional in the sense that every service can be answered with Skip  -  but the ASKING is not optional: the Step 3 sequential loop still walks every missing service one by one (token → author → host). Phase 0 re-asks at runtime only for tokens the user skipped here.

### Step 8  -  Enforcement hooks (optional, Claude Code)

Offer to merge `$HOME/.claude/templates/claude-hooks.json`: three `PreToolUse` gates that block on a non-zero exit (secret scan, agent-guard, read-size) plus three capture hooks that block nothing (`PreCompact`, `SessionEnd`, `SessionStart`). What each does: `$HOME/.claude/multi-agent-refs/picker-contract.md`.

- Ask (picker): "Install the pipeline's hooks into `~/.claude/settings.json`?" Default Yes.
- On Yes, deep-merge EVERY event in the template's `hooks` object, not `PreToolUse` alone - merging one event silently drops the capture hooks. Preserve existing hooks; never duplicate a matcher already calling the same script.
- Say what the merge does NOT cover: only the three gates need no run-specific arguments, so only they are hookable; the rest are phase-enforced.
- Say what it does not turn on: the read-size gate is inert until `prefs.global.bulkRead.mode` is set. Recommend `observe` first. Why, and the Phase 3 exemption: `$HOME/.claude/multi-agent-refs/picker-contract.md`.

### Step 9  -  Default stack plugin enablement

Stack skills ship as versioned plugins in the `{owner}/multi-agent-plugins` marketplace. On first setup, wire the stack so the pipeline works out of the box.

1. Ensure the marketplace is known (idempotent):
   ```bash
   bash "$HOME/.copilot/scripts/plugin-refresh.sh" {owner} --add-only
   ```
2. Ask the detector, then derive (no marker list here; a second list would drift from the detector):

   ```bash
   eval "$(bash $HOME/.copilot/lib/stack-detect.sh "$PROJECT_ROOT")"   # MA_STACKS, MA_STACK_WHY
   node -e 'import(process.env.HOME+"/.copilot/scripts/_stack-routing.mjs").then(m=>
     console.log(m.pluginsForStacks(process.argv[1].split(" ").filter(Boolean)).plugins.join(" ")))' "$MA_STACKS"
   ```

   Report `MA_STACK_WHY` with the result. `ai-common-toolkit` and
   `ai-analyst-toolkit` are in every answer, the empty one included: neither is
   stack-specific. **No marker matched is an answer** - those two, not a guess.
   A name the marketplace does not carry is dropped with `dropped[]` naming it,
   so a toolkit that was never shipped is caught before it fails to load.
3. Report the enabled set. To change later, run `/multi-agent-stack <ios|android|web|backend|...>` in the repo. Pipeline Phase 1 auto-detects the stack for its own routing regardless of enablement.

The marketplace name (`multi-agent-plugins`) is generic; a different org points `{owner}` at its own fork.
