---
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."
description-tr: "İlk kurulum sihirbazı: keychain token keşfi, Git Identity kaydı ve pipeline hazırlığı."
allowed-tools: Bash, Read, Write, AskUserQuestion, WebFetch
---

## Setup (Credential Store + Git Identity Onboarding)

Cross-platform setup  -  uses `$HOME/.claude/lib/credential-store.sh` to read/write secrets in the platform-native credential store:

| Platform | Backend | Built-in? |
|----------|---------|-----------|
| macOS    | Keychain (`security`) | Yes |
| Windows  | Credential Manager (PowerShell `CredentialManager` module) | Module install required |
| Linux    | libsecret (`secret-tool`) | Distro package required |

Run `~/.claude/lib/credential-store.sh doctor` first  -  it prints platform + missing dependencies with the exact install command.

On macOS and Linux the shell driver auto-delegates `get`/`set`/`delete` to the deterministic Python helper at `$HOME/.claude/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:

| Axis | What it controls | Configurable? |
|---|---|---|
| `promptLanguage` | The picker's structural UI chrome only: `AskUserQuestion` `label` (button text) and `header` (chip), plus host error UI and internal contract identifiers | **No  -  fixed to `en`.** Button and chip text stays English so tooling reads the same across CLIs. |
| `outputLanguage` | The assistant's non-interactive explanations, status updates, error messages, and pipeline-generated reports rendered to the user | Yes  -  set here and 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).

#### Step 0 prompt  -  `outputLanguage`

The setup 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 this point forward, every prompt in Steps 1-6 below follows the canonical
per-field matrix in `$HOME/.claude/multi-agent-refs/rules.md` ("Language Application"):
the `question` text and each option's `description` render in `outputLanguage`, while
`label` and `header` stay English. The wizard's status text and post-setup summary
render in `outputLanguage`. External payloads remain English.

> This paragraph used to say every prompt renders in English "per the fixed
> `promptLanguage`", which contradicted the canonical matrix and produced
> half-English pickers on Turkish runs: `promptLanguage` governs only the button and
> chip chrome, never the question a user reads.

### Step 0.5  -  Credential Backend Check

Before discovery, verify the platform's credential backend is available:

```bash
~/.claude/lib/credential-store.sh doctor
```

| Output | Action |
|--------|--------|
| `Backend: ✓` | Continue to Step 1 |
| Linux: `secret-tool ✗ MISSING` | Prompt: `sudo apt install libsecret-tools` (Debian/Ubuntu) or `sudo dnf install libsecret` (Fedora). Re-run setup after install. |
| Windows: `CredentialManager ✗ MISSING` | Prompt: `Install-Module CredentialManager -Scope CurrentUser -Force` in an elevated PowerShell. Re-run setup. |
| Platform `unknown` | Halt  -  credential storage cannot proceed. |

### 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

Instead of checking only hardcoded key names, discover ALL user keys and intelligently map them to services. Uses cross-platform `credential-store.sh list`:

**1a. Discover all keys:**
```bash
DISCOVERED=$(~/.claude/lib/credential-store.sh list 2>/dev/null | grep -i "^${USER}" | sort -u)
echo "$DISCOVERED"
```

**1b. Search keywords (per service):**

Standard key names  -  the ones to use when creating a NEW key  -  live in `$HOME/.claude/multi-agent-refs/keychain.md` "Standard key names"; read the name for a service from there. Existing keys under other names are perfectly valid, which is what these keywords are for.

| Service ID | Search Keywords |
|------------|-----------------|
| `jira` | `jira` |
| `bitbucket_token` | `bitbucket` + (`token` or `access` or `pass`) |
| `bitbucket_user` | `bitbucket` + (`user` or `name`) |
| `github` | `github` or `gh` (exclude `ssh`) |
| `confluence` | `confluence` |
| `figma` | `figma` + (`token` or `pat` or `access`) + NOT `mcp` |
| `figma_mcp` | `figma` + `mcp` |
| `fortify` | `fortify` |
| `graylog` | `graylog` |
| `graylog_test` | `graylog` + `test` |
| `firebase` | `firebase` (any variant: `sa`, `service`, `account`, `access`, `json`) |
| `jenkins` | `jenkins` |
| `appstore_connect_key_id` | (`appstore` or `asc` or `app_store`) + (`key` or `keyid`) |
| `appstore_connect_issuer_id` | (`appstore` or `asc` or `app_store`) + `issuer` |
| `appstore_connect_apple_id` | (`appstore` or `asc`) + (`apple` or `account` or `user`) |
| `appstore_connect_password_item` | (`appstore` or `asc` or `altool`) + (`password` or `app_specific`) |

> Multi-agent setup onboards **both** Figma tokens because the pipeline's 3-tier Figma access chain (see `$HOME/.claude/rules/figma-pipeline.md`) uses them in priority order: Tier 1 = MCP (`figma_mcp`), Tier 2 = REST PAT (`figma`), Tier 3 = user screenshot fallback. Missing either token forces a downshift; missing both leaves Tier 3 as the only path. The Tier 2 logical key is `figma`, not `figma_pat`: `figma_pat` is the pre-v13.6 name that `migrate-prefs.mjs` consolidates away, and writing a new mapping under it means the next migration deletes it.

> The four App Store Connect entries are **iOS-only and optional**  -  skipping them all reports Gate 2 of `/multi-agent:store-ready` as `SKIPPED`, never as a pass. Two hold identifiers rather than secrets and one holds a keychain ITEM NAME, yet all four go through the mapping layer so every credential is read the same way. Tiers, the role constraint that decides which to offer first, and the onboarding mechanics: Step 3b and `$HOME/.claude/multi-agent-refs/setup/app-store-connect.md`.

**1c. Resolution logic (per service):**

For each service in the table above, follow this order:

1. **Check standard key** -> `~/.claude/lib/credential-store.sh get "<standard_key>" 2>/dev/null`
   - Found (non-empty stdout, exit 0) -> auto-map, done
3. **Search discovered keys** -> filter `$DISCOVERED` by the service's search keywords (case-insensitive)
   - Exactly 1 match -> show: `"Found '${matched_key}' for ${service}  -  use this? (enter=yes / n=skip)"` -> if yes, map it
   - Multiple matches -> show numbered list, user picks:
     ```
     Multiple keys found for GitHub:
       1. ${USER}_Github
       2. ${USER}_Github_Access_Token_Old
       3. ${USER}_Github_Access_Token
     Select [number] or skip:
     ```
   - 0 keyword matches -> go to Step 4
4. **Suggest candidates from full keychain** -> When keyword search yields 0 results, do NOT immediately mark as MISSING. Show broad candidates from `$DISCOVERED_ALL` and ask the user:
   ```
   No exact match found for Jira in your keychain.
   
   These entries might be your Jira token  -  do any look right?
     1. MyPersonalJiraToken
     2. work_jira_pat
     3. JiraPAT_2024
     n. None of these (skip)
   
   Select [number] or n to skip:
   ```
   
   Candidate selection:
   - Show entries that partially match the service name, common token suffixes (`token`, `pat`, `key`, `secret`, `access`, `password`), or relaxed keyword matching (substring instead of prefix)
   - Exclude entries already mapped to other services and system entries (Apple, com.apple.*, iCloud, etc.)
   - Cap at 10 candidates, sorted by relevance (shortest edit distance to service keywords)
   - If 0 candidates even after broad search -> MISSING (truly not in keychain)

**Important:** Steps 3-4 search the FULL `$DISCOVERED` list (both passes from 1a), not just user-prefixed entries. This catches tokens saved under any naming convention.

**1d. Check preferences for existing mapping:**

Before running discovery, check `prefs.global.keychainMapping`. If a mapping already exists for a service AND the mapped key still exists in Keychain -> skip discovery for that service (show as already mapped from preferences).

If a previously mapped key NO LONGER exists in Keychain -> clear the stale mapping, re-run discovery for that service.

### 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
```

Non-standard key names are perfectly valid  -  the mapping handles resolution. No rename or migration is offered.

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 token (optional, opt-in)

Only relevant when the admin has issued this user a token. Since v15.8.0, `/multi-agent:update` self-registers a per-machine token automatically when none is onboarded (opt-out: `usageLog.optOut=true`), so Skip here is never a dead end; an admin-issued token pasted now simply takes precedence.

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 the secret in the credential store ONLY - never in a file, prefs value, git, or any synced/published tree. Use the standard per-user key name so it is revocable independently and consistent across the user's machines:

```bash
~/.claude/lib/credential-store.sh set "${USER}_Usage_Ingest_Token" "<pasted-token>"
```

Then map it and enable logging (the token itself stays in the credential store; only the logical mapping + the on-switch land in prefs):

```bash
node -e '
  const fs=require("fs"),os=require("os"),p=os.homedir()+"/.claude/multi-agent-preferences.json";
  const j=JSON.parse(fs.readFileSync(p,"utf8"));
  j.global=j.global||{}; j.global.keychainMapping=j.global.keychainMapping||{};
  j.global.keychainMapping.usage_ingest=process.argv[1];
  j.global.usageLog=Object.assign({enabled:true},j.global.usageLog||{},{enabled:true});
  fs.writeFileSync(p,JSON.stringify(j,null,2)+"\n");
' "${USER}_Usage_Ingest_Token"
echo "  -> operational reporting configured (token in credential store)"
```

Security notes to surface to the user: the token is **write-only** (append-only to the endpoint - no read access, no other scope), stored **only in the OS credential store**, and **per-user** so the admin can revoke this one token without affecting anyone else. `usage-report.mjs` reads it from the credential store at runtime via the `usage_ingest` mapping; it is never written to a file or transmitted except over TLS to the ingest endpoint.

### 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?

  Existing identities:
    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)

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)

This flow is used in TWO contexts:
- **Setup Step 4**  -  for missing tokens during first-time onboarding
- **Phase 0 token pre-check**  -  when a token is missing or expired (401/403) at runtime

**Trigger**: token missing (`keychainMapping` is `null`) OR token expired (API returned 401/403).

**Step A  -  Ask** (options depend on whether the token is missing or expired):

Missing token (first-time / unmapped):
```
{Service} token missing.
  Where to get it: {source URL/instructions}

  [1] Add now  -  I'll generate the token and copy it
  [2] Skip  -  I'll set it up later

  Select:
```

Expired / rejected token (401 / 403 mid-run)  -  the Expired-token decision from `$HOME/.claude/multi-agent-refs/keychain.md` Rule 1:
```
{Service} token expired (the service returned 401/403).
  Where to get a fresh one: {source URL/instructions}

  [1] Regenerate  -  replace {KEY_NAME} in place, then retry
  [2] Use a different token  -  map another Keychain entry, then retry
  [3] Skip and continue  -  drop this source (halts only if the token is required for the input)

  Select:
```

Render `question` + `description` in `outputLanguage`; `label` + `header` English. The picked label is a choice, never the secret  -  the value still enters via Step B clipboard.

- **Skip** (missing `[2]` / expired `[3]`) → skip semantics. During setup: `keychainMapping` stays `null`. During Phase 0 / mid-run: abort that service's contribution, warn and continue if non-critical, halt if critical (e.g. Jira token for Jira input).
- **Add now** / **Regenerate** → proceed to Step B (clipboard save). Regenerate reuses the same `{KEY_NAME}`.
- **Use a different token** → proceed to Step B, then re-run Step C identity binding for the new entry.

**Step B  -  Clipboard save** (token NEVER appears in terminal):

For PAT / API key / username:
```
  Copy the token to your clipboard, then press Enter.
  (Token will be read from clipboard and saved to Keychain automatically)

  Press Enter when ready...
```

Pipeline runs silently  -  value is piped via stdin (`-` sentinel) so it never lands on argv / shell history:
```bash
pbpaste | ~/.claude/lib/credential-store.sh set "<KEY_NAME>" "$(cat)"
pbcopy < /dev/null
```

(The shell driver auto-delegates to `~/.claude/scripts/keychain.py` on macOS / Linux, which writes both `-l` (label) and `-s` (service) attributes so callers using either convention find the same entry.)

For JSON file (Firebase)  -  same clipboard flow, JSON content never lands in argv or shell history:
```
  Open the Service Account JSON, copy its FULL contents to your clipboard,
  then press Enter.
  (Contents will be base64-encoded and saved to Keychain automatically)

  Press Enter when ready...
```

Pipeline runs silently:
```bash
pbpaste | base64 | ~/.claude/lib/credential-store.sh set "<KEY_NAME>" "$(cat)"
pbcopy < /dev/null   # clear clipboard
```

Linux substitutes: `xclip -selection clipboard -o` or `wl-paste` for `pbpaste`; matching clear command for `pbcopy < /dev/null`. The shell driver detects platform and routes accordingly (see `~/.claude/lib/credential-store.sh`).

**Firebase repeats per project.** Teams own several Firebase projects (legacy plus redesign, staging plus prod), each with its own key, and a crash URL from the second one used to fail the `project_id` check as if it were misconfigured. After each saved key, read `project_id` from the decoded JSON (never ask) and offer another round: `Saved: <project_id>. Add another Firebase project? [y/N]`.

- Every key appends `{projectId, keychainKey, label?}` to `prefs.global.firebase.accounts`; the first one also fills `keychainMapping.firebase`, so a one-project setup is unchanged.
- Keys after the first are named `${USER}_Firebase_Access_Json_<projectId>` so they cannot collide.
- At run time `fetch-crashlytics.sh` picks the account matching the console URL's projectId, falling back to `keychainMapping.firebase`.

For GitHub (special case):
```
  ! gh auth login
```

**Step C  -  Identity binding** (after token saved):

```
  ✓ Saved to Keychain as <KEY_NAME>
  ✓ Clipboard cleared

  Which git identity uses this token?

    Existing identities:
      1. Ada Lovelace <ada@example.com>
      2. Ada Lovelace <ada@personal.com>
      n. Create new identity

    Select:
```

- Existing identity selected → add `{service}: <KEY_NAME>` to that identity's `servicePatMap`
- `n` (create new) → ask name + email → create identity → add to `prefs.global.identities[]` → map token
- First token ever (no identities exist) → skip picker, go straight to name + email
- `bitbucket_user` is excluded from identity binding (it's a username, not a PAT)

**Step D  -  Confirm + update**:

```
  ✓ Token mapped to identity: Ada Lovelace <ada@example.com>
```

Update `prefs.global.keychainMapping.{service}` with the key name.
Update `identity.servicePatMap.{service}` with the key name.
Update `serviceStatus.{service} = { ok: true, checkedAt: <now> }`.

**→ Next: if `{service}` is `jira` / `confluence` / `bitbucket` / `fortify` / `graylog` AND the corresponding
`prefs.global.hosts.{service}` is unset, run Step 3.5 (Host Prompt) inline before
the Auto-routing rule below. required  -  the pipeline cannot build API URLs without
the host. Firebase is exempt (see Step 3.5).**

**Auto-routing rule** (optional, on first token per platform):

If `platformIdentityRouting` has no rule for this platform yet, auto-suggest one:
```
  Auto-add routing rule?
    bitbucket.example.com/* → Ada Lovelace <ada@example.com>
    [enter=yes / n=skip]
```

This builds `platformIdentityRouting` incrementally  -  no separate Step 7 needed for most users.

---

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

Runs inside Step 3 alongside the other missing credentials. The four `appstore_connect_*` entries, their non-secret handling (three hold identifiers / a keychain item name, not pasteable secrets), the API-key vs Apple-ID tier picker, and the verification probes live in `$HOME/.claude/multi-agent-refs/setup/app-store-connect.md`. Read that file when the user opts into App Store Connect onboarding; on Skip, nothing is written.

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

**Not a standalone step**  -  runs inline at the end of the Token Save Flow whenever the saved token belongs to a **hosted service** (Jira, Confluence, Bitbucket, Fortify, Graylog) AND the host is not yet in `prefs.global.hosts`. Firebase tokens skip this step  -  Crashlytics is always on Google's fixed domains and `project_id` is embedded in the service-account JSON.

Right after Step D (identity mapping confirmed), ask:

```
Service host  -  needed to build API URLs for this token.

  {Service} host (e.g. jira.example.com): ___
```

Save to `prefs.global.hosts.{service}`. For Fortify, also ask `Fortify project version ids (optional, comma-separated - e.g. 1234,5678)` → `prefs.global.fortify.versionIds`. A ticket naming only an instance id carries no version, so the lookup silently no-ops without these; URL-referenced findings resolve either way.

For Jira, **discover the project keys instead of asking the user to recall them.** The token is already saved and the host is already known, so ask Jira which projects this person actually works in - a corporate instance has thousands of projects, and a typed key is a typo waiting to route branches and new issues at the wrong board:

```bash
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://{JIRA_HOST}/rest/api/2/search?jql=assignee%3DcurrentUser()%20OR%20reporter%3DcurrentUser()%20ORDER%20BY%20updated%20DESC&fields=project&maxResults=100" \
  | jq -r '[.issues[].fields.project | "\(.key)\t\(.name)"] | unique | .[]'
```

Present the distinct keys, most-recently-worked first, as a picker; fall back to the free-text prompt `Default Jira project key (e.g. PROJ)` when the call fails or returns nothing (no VPN, fresh account). The picked key → `prefs.global.defaultJiraKey`. From the Jira host, derive `corpDomain` (`jira.example.com` → `example.com`) and pre-fill it so the Confluence / Bitbucket prompts only need the subdomain.

`defaultJiraKey` is a single global fallback, but one Jira host usually serves several repos with DIFFERENT keys, so the pass continues with a per-repo mapping. Show the repos, and let the key come from the discovered list rather than being typed again:

```
Per-repo Jira keys (optional)  -  repos whose project key differs from {defaultJiraKey}:

  [ ] 1. my-ios-app        (current: PROJA)
  [ ] 2. my-other-app      (current:  - )

  Toggle repos, then pick a project for each  -  discovered: PROJA, PROJB, PROJC. Enter to skip.
```

- The repo list comes from `prefs.projects` + `recentProjects` (when Step 5 already ran); a free-text repo path is accepted for repos not discovered yet, and a free-text key for a project the search did not surface.
- Each answer is PREPENDED to `prefs.projects[{slug}].jiraProjectKeys` (deduped, max 10 per schema). Example: `my-ios-app` → `PROJA`, `my-other-app` → `PROJB`  -  two repos on the same Jira host, two different project keys.
- Resolution order everywhere a Jira key is needed (placeholder replacement, branch names, new-issue creation): `prefs.projects[{slug}].jiraProjectKeys[0]` first, `global.defaultJiraKey` as fallback.
- **Re-run / update**: `/multi-agent:setup jira-keys` re-opens only this mapping without touching tokens or hosts.

Resulting shape:

```json
{
  "global": {
    "hosts": {
      "jira":       "jira.example.com",
      "confluence": "confluence.example.com",
      "bitbucket":  "bitbucket.example.com",
      "fortify":    "ssc.example.com",
      "graylog":    "graylog.example.com",
      "graylogTest": "graylog-test.example.com",
      "corpDomain": "example.com"
    },
    "defaultJiraKey": "PROJ"
  }
}
```

Placeholders `{JIRA_HOST}`, `{CONFLUENCE_HOST}`, `{BITBUCKET_HOST}`, `{FORTIFY_HOST}`, `{GRAYLOG_HOST}`, `{GRAYLOG_TEST_HOST}`, `{CORP_DOMAIN}`, `{JIRA_KEY}` in skills/commands are resolved from this block at runtime. If a host is ever missing when a phase needs it, Phase 0 prompts the same Host Prompt inline.

Graylog uses a **PAT** whose API auth is HTTP Basic with the token as username and the literal `token` as password  -  no separate username entry. It is optional by the null-mapping convention: `keychainMapping.graylog` or `hosts.graylog` left `null` degrades the log fetch to empty and never blocks a run.

Graylog is asked **twice**  -  test and production are separate instances, and a tester-minted trx id does not exist in production. After the production host:

```
Graylog test host (optional  -  Enter to skip): ___
Does the test instance use a different token? [y/N]
```

Test host → `hosts.graylogTest` (`{GRAYLOG_TEST_HOST}`); skipped means production-only search, which degrades nothing. Yes to the token question runs one more Token Save Flow pass for `graylog_test`; no (default) leaves it null and the fetcher reuses the production key. Run-time order is `--env auto`: production first, test on empty or unreachable, and the payload names which answered.

**Re-run / update**: `/multi-agent:setup hosts` re-opens the prompt to edit values without touching tokens.

Team-sharing note: Hosts are the only corporate metadata the pipeline needs. Tokens live in macOS Keychain (encrypted, never on disk inside the repo). Preferences file lives in `~/.claude/`  -  fully outside the repo clone. A teammate cloning the repo runs `/multi-agent:setup` once; host + token prompts run naturally together.

---

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

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

Use case: a single user with two corporate identities (e.g. agency + client). Each account points at its own Jira host  -  `prefs.global.hosts.jira` can hold only one default, so per-account overrides win for the rest.

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?

  1) Use global default
  2) Set per-account host
```

If `2`, prompt for `jiraHost` / `bitbucketHost` / `confluenceHost` (each optional). Save to `prefs.global.accounts[]`:

```json
{
  "global": {
    "accounts": [
      { "id": "personal", "jiraHost": "personal.atlassian.net" },
      { "id": "client-x", "jiraHost": "client-x.atlassian.net", "bitbucketHost": "bitbucket.client-x.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`.

**Re-run / update**: `/multi-agent:setup accounts` opens the editor for this block 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.

Optional per-project features (configured on first use, nothing to do now):
  • Phase 7 Report Step 2 Wiki  -  auto-generates component wiki pages + Figma
    screenshots. Activates when (a) task is a component AND (b) a Figma token
    is in Keychain. Four adapters supported: submodule / in-repo / github-wiki
    / separate-repo. First run asks: use auto-detected path, use a custom
    path, skip once, or disable for this project (persisted).
  • channels  -  post-hoc multi-channel reporter (Jira/Confluence/Wiki/PR), works on any branch with an open PR.
  • Default reviewers  -  auto-fetched from Bitbucket default-reviewers endpoint
    or GitHub CODEOWNERS on first PR.

Next step:
  /multi-agent "PROJ-12345"              -> start your first task
  /multi-agent help                      -> 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. Three independent sources merge into one flat list: local `$HOME` scan (depth 3, repo markers), Bitbucket (last 90 days, token-gated), GitHub (owner + collaborator, push in last 90 days, token-gated). Any source failing skips that source only, is reported (never swallowed), and discovery is never destructive and idempotent.

```
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 ("update token resolver in `common` + `uicomponents`") becomes a single keypress next time.

Auto-skipped if Step 5 was skipped or fewer than 2 repos were selected.

**Prompt:**
```
Step 6  -  Remember as a multi-repo group (optional)
You picked 2 repos: Booking, UIComponents.

Save this combo for one-tap selection in Phase 0?
  Group name (Enter for auto): [Booking + UIComponents]
  Skip with 'n'.
```

**Auto-name heuristic** (suggested but editable):
- 2 repos    → `"<repoA> + <repoB>"`
- 3-4 repos  → `"<repoA>, <repoB>, <repoC>(+1)"`
- 5+ repos   → `"<repoA> + <count-1> more"`

Repo identifiers in the label are the human label from `recentProjects` (basename of the path, not the full path), so groups stay readable.

**Save** (matches schema `global.recentGroups[]` shape  -  `repos` required, `label` / `count` / `lastUsed` optional):

```json
{
  "global": {
    "recentGroups": [
      {
        "label": "Booking + UIComponents",
        "repos": ["~/dev/Booking", "~/dev/UIComponents"],
        "count": 1,
        "lastUsed": "2026-04-15T21:08:00Z"
      }
    ]
  }
}
```

**LRU + dedup rules:**
- New group → unshift to front of `recentGroups`.
- Same set of `repos` already exists (order-independent compare) → bump existing entry to front, increment its `count`, update `lastUsed`. Do NOT create a duplicate.
- Schema cap is 10 entries; oldest LRU entries past the cap are dropped.

**Skip rules:**
- Single-repo selection → step is a no-op (no group needed for one repo).
- User answers `n` → not saved; the picker still works for one-time use, and a future Phase 0 selection of the same combo will re-prompt.
- Step 5 skipped → Step 6 is also skipped (no selection to remember).

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

Map repo URL **glob patterns** to a named identity from `prefs.global.identities`. Phase 0 consults this map in Step 5 to auto-pick the git author and the right `servicePatMap` for the target repo  -  preventing corporate identity leakage into personal repos and vice versa.

Auto-skipped if `identities` has fewer than 2 entries (single-identity case has nothing to route).

**9a. Auto-suggest from `servicePatMap` ownership**

For each identity, derive a default routing rule from where its PATs work:
- Identity owns `bitbucket` PAT → propose `<bitbucket-host>/*` glob, mapped to that identity.
- Identity owns `github` PAT → propose `github.com/<gh-username>/*` if known (from `identities[].username`), else `github.com/*`.
- Identity owns `confluence` / `jira` only → no URL routing applies (those aren't repo origins)  -  skip.

Auto-suggestions are **proposals**, not forced. User confirms or edits each one.

**9b. Confirm or edit each suggested rule**

```
Step 7  -  Platform Identity Routing (optional)
Auto-suggested rules:

  1. bitbucket.{corp-domain}/*                  -> corporate    [confirm/edit/skip]
  2. github.com/{personal-username}/*           -> personal     [confirm/edit/skip]
  3. github.com/{org-name}/*                    -> corporate    [confirm/edit/skip]

Add another rule? [Add another / Done]
```

Each rule answer:
- `Enter` or `c` → confirm
- `e` → edit (prompt for new glob and/or new identity name)
- `s` → skip this rule
- `n` (final question) → finish

**9c. Save**

```json
{
  "global": {
    "platformIdentityRouting": {
      "bitbucket.{corp-domain}/*": "corporate",
      "github.com/{personal-username}/*": "personal",
      "github.com/{org-name}/*": "corporate"
    }
  }
}
```

Glob matching semantics (Phase 0 implementation):
- `*` matches any characters except `/`
- `**` matches any characters including `/`
- Most-specific match wins (longer literal prefix > shorter)
- No match → Phase 0 falls back to the interactive identity picker

**9d. Skip / degrade rules**

- Zero rules saved is valid  -  Phase 0 always falls back to the identity picker.
- A rule may point to an identity name that doesn't (yet) exist in `identities`; on Phase 0 lookup, an unknown identity name surfaces as a warning and the picker is shown.
- Step 7 is idempotent  -  re-running merges new rules; existing rules are preserved unless the user explicitly edits them.
- Routing is **advisory**: the user can always override the auto-picked identity in Phase 0.

### Cross-Platform Notes (Step 1-9)

The credential store is platform-agnostic  -  every read/write goes through `~/.claude/lib/credential-store.sh`, which dispatches to the right backend (`security` on macOS, `secret-tool` on Linux, PowerShell `CredentialManager` on Windows). On macOS / Linux the shell driver delegates to `~/.claude/scripts/keychain.py` for deterministic behaviour. You almost never need the platform-native commands directly.

Clipboard helpers (`pbpaste`, `pbcopy`) are macOS-specific  -  for Copilot CLI on Linux substitute:

| macOS command | Linux equivalent | Notes |
|---|---|---|
| `pbpaste` | `xclip -selection clipboard -o` (X11) or `wl-paste` (Wayland) | Install with `apt install xclip` or `apt install wl-clipboard` |
| `pbcopy < /dev/null` | `xclip -selection clipboard < /dev/null` or `wl-copy --clear` | Same intent: clear clipboard after token paste |

If you need to bypass the helper (debugging, raw inspection), the underlying platform commands are documented at the top of `$HOME/.claude/lib/credential-store.sh`. Otherwise stay on the helper  -  it keeps secrets off argv (stdin sentinel `-`) and writes both `-l` and `-s` attributes on macOS.

If no backend is available, setup falls back to a plain-text prompt + a warning that the token is **not persisted**  -  pipeline phases will re-ask each session.

### Token -> Source Mapping

Standard key names (used when creating NEW tokens). Discovery may find existing keys under different names  -  that's fine, the mapping in preferences handles it.

Standard key names and token types: `$HOME/.claude/multi-agent-refs/keychain.md` "Standard key names". Where to generate each one:

| Service ID | Where to Get |
|------------|---------------|
| `jira` | Jira -> Profile -> Personal Access Tokens (VPN required) |
| `bitbucket_token` | Bitbucket -> Personal settings -> App passwords |
| `bitbucket_user` | Bitbucket profile username (plain text, not a PAT) |
| `github` | GitHub Settings -> Tokens (scopes: repo, read:org, project) |
| `confluence` | Confluence -> Profile -> Personal Access Tokens (VPN required) |
| `figma` | Figma Developer Settings (max 90 days). Tier 2 / REST. Prompt copy: "Figma Personal Access Token (for Tier 2)". |
| `figma_mcp` | Automatic via Claude Code Figma MCP remote auth. Tier 1 / MCP. Prompt copy: "Figma MCP token (for Tier 1)". |
| `fortify` | Fortify SSC -> Token Management (VPN required) |
| `graylog` | Graylog -> System -> Users and Teams -> Edit Tokens (VPN required) |
| `graylog_test` | Same page on the TEST instance; optional |
| `firebase` | Firebase Console -> Project settings -> Service accounts -> Generate new private key. One key per Firebase project; `project_id` is parsed from the decoded JSON, never asked. |
| `jenkins` | Jenkins -> User -> Configure -> API Token |
| Git Identities | Stored in `$HOME/.claude/multi-agent-preferences.json`, not Keychain |

### Inline Token Save Pattern

All tokens go through `~/.claude/lib/credential-store.sh`, which auto-delegates to the deterministic Python helper (`~/.claude/scripts/keychain.py`) on macOS / Linux and to the PowerShell `CredentialManager` module on Windows. The Python path writes both `-l` (label) and `-s` (service) attributes so existing manually-added entries with either convention remain findable.

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

# Plain text (direct value)
~/.claude/lib/credential-store.sh set "<SERVICE_NAME>" "<VALUE>"

# JSON file from clipboard (preferred  -  no file path leaks into history)
pbpaste | base64 | ~/.claude/lib/credential-store.sh set "<SERVICE_NAME>" "$(cat)"
pbcopy < /dev/null

# JSON file from disk (fallback when clipboard backend unavailable)
~/.claude/lib/credential-store.sh set "<SERVICE_NAME>" "$(base64 < /path/to/file.json)"

# Read
~/.claude/lib/credential-store.sh get "<SERVICE_NAME>"

# Decode JSON
~/.claude/lib/credential-store.sh get "<SERVICE_NAME>" | base64 -d
```

For maximum stdin hygiene (the secret never lands on argv / shell history), use the Python helper directly with the `-` sentinel:

```bash
pbpaste | ~/.claude/scripts/keychain.py set "<SERVICE_NAME>" -
```

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

The `figma_mcp` pass of the Step 3 loop opens with a mode question BEFORE any token is collected:

```
Figma MCP mode?

  [1] Remote  -  mcp.figma.com, OAuth token (figu_)  (recommended)
  [2] Local   -  @anthropic-ai/figma-mcp via npx, uses the Figma PAT
```

**Remote** (`figu_` OAuth token  -  generate it FOR the current user, never just point at docs):
- No OAuth app and no client_id/secret needed: Dynamic Client Registration against `api.figma.com/v1/oauth/mcp/register` + PKCE, the browser opens, the user clicks Allow, and the resulting access + refresh token pair is saved to the credential store under a name the user confirms. Map that entry to `keychainMapping.figma_mcp` (Token Save Flow Step D applies as usual).
- If `prefs.global.figmaMcp.remoteGeneratorScript` is set, run that script  -  it drives the whole flow end to end (registration, browser, keychain save) for the current user. Otherwise guide the flow inline.
- Renewal is silent afterwards via `$HOME/.claude/lib/figma-mcp-refresh.sh` (refresh token). Never echo token values.

**Local** (PAT-based):
- Validates the Figma PAT against the REST API, saves it to the credential store (reusing the `figma` mapping when present), and writes the `@anthropic-ai/figma-mcp` server block into Claude Code `settings.json` `mcpServers`:
```bash
# stdin sentinel keeps the value out of shell history
pbpaste | ~/.claude/scripts/keychain.py set "${USER}_Figma_Access_Token" -
pbcopy < /dev/null
# then: ~/.claude/settings.json -> mcpServers -> figma
```
- If `prefs.global.figmaMcp.localGeneratorScript` is set, run that script instead.

Choosing Remote does NOT skip the `figma` PAT pass (Tier 2 still uses the PAT); choosing Local leaves `keychainMapping.figma_mcp` null and Tier 1 unavailable until Remote is onboarded.

### 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

To set up multi-agent on a new machine:

```
1. Install the toolkit:
   npx @{npm-scope}/multi-agent-pipeline install

2. Start setup:
   /multi-agent setup

3. 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"
   /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 hook (optional, Claude Code)

Offer to make the secret scan a HARD pre-commit gate (a non-zero exit blocks the commit) instead of an advisory step. The recommended block ships at `install/templates/claude-hooks.json`.

- Ask (picker): "Install the pre-commit secret-scan hook into `~/.claude/settings.json`?" Default Yes.
- On Yes, deep-merge the template's `hooks.PreToolUse` into the user's `settings.json` (preserve any existing hooks; do not duplicate a matcher that already calls `pre-commit-check.sh`).
- Honest note to show: this is the only deterministic gate that is OS-enforceable as a hook (it needs no run-specific arguments). The evidence / consensus / intent / learnings gates are invoked by the pipeline phases with per-run arguments, so they are enforced by the phase contract + the installed gate scripts, not by a hook.
### 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
   claude marketplace add {owner}/multi-agent-plugins 2>/dev/null || true
   ```
2. Detect the project stack from markers and enable the matching plugin(s) plus the two always-on plugins in the project's `.claude/settings.json` `enabledPlugins`:
   - `.xcodeproj` / `Package.swift` / `*.xcworkspace` → `ai-ios-toolkit`
   - `build.gradle` / `settings.gradle` → `ai-android-toolkit`
   - `package.json` with `react`/`next` → `ai-frontend-toolkit`
   - `requirements.txt` / `pyproject.toml` / server `package.json` → `ai-backend-toolkit`
   - **no clear marker → default `ai-ios-toolkit`**
   `ai-common-toolkit@multi-agent-plugins` and `ai-analyst-toolkit@multi-agent-plugins` are always set `true` alongside the stack plugin: neither is stack-specific.
3. Report the enabled set. To change later, run `/multi-agent:stack <ios|android|frontend|backend|...>` in the repo. Pipeline Phase 1 auto-detects the stack for its own routing regardless of enablement.

The marketplace repo name (`multi-agent-plugins`) is generic; a different org points `{owner}` at its own fork - nothing in the pipeline is coupled to a specific account.
