@mmerterden/multi-agent-pipeline 15.6.1 → 15.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/pipeline/commands/multi-agent/help/SKILL.md +14 -7
- package/pipeline/commands/multi-agent/setup/SKILL.md +65 -113
- package/pipeline/multi-agent-refs/setup/repo-discovery.md +116 -0
- package/pipeline/schemas/prefs.schema.json +36 -1
- package/pipeline/scripts/build-stack-plugins.mjs +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,20 @@ Internal file-layout changes that don't affect the slash-command surface are sti
|
|
|
16
16
|
|
|
17
17
|
## [Unreleased]
|
|
18
18
|
|
|
19
|
+
## [15.7.0] - 2026-08-19
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- **Setup walks missing credentials one by one**: Step 3 gained a strict sequential onboarding loop - fixed service order, one Token Save Flow prompt per service (token -> author -> host), per-service skips that never abort the loop, and `figma` / `figma_mcp` as distinct passes. The discovery summary can no longer end setup.
|
|
23
|
+
- **Per-repo Jira project keys in setup**: the Jira pass closes with an optional multi-select mapping (repo -> project key) written to `prefs.projects[{slug}].jiraProjectKeys`; per-repo keys resolve before `global.defaultJiraKey` everywhere a key is needed. Re-open with `/multi-agent:setup jira-keys`.
|
|
24
|
+
- **Figma MCP mode question**: the `figma_mcp` pass opens with Remote / Local. Remote generates the `figu_` OAuth token for the current user (Dynamic Client Registration + PKCE; `prefs.global.figmaMcp.remoteGeneratorScript` drives the flow when set); Local wires the PAT-based `@anthropic-ai/figma-mcp` server (`localGeneratorScript` supported). Prefs schema: new `global.figmaMcp` block plus `supabase_access` / `supabase_service_role` keychainMapping keys.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- **Help spells out the --dev pipeline**: the Pipeline section now carries the dev chain (Phases 1-2 skipped, Review never skipped, Opus dev). Stale notes claiming Test or Review are skipped in --dev were corrected in both languages.
|
|
28
|
+
- **Setup Step 5 (Repo Discovery) split to a reference**: the full contract moved to `multi-agent-refs/setup/repo-discovery.md`; the SKILL keeps the opt-in prompt and a summary.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
- **build-stack-plugins.mjs dead import**: unused `APPLE_ONLY` import removed (superseded by the `STACK_ONLY` routing table).
|
|
32
|
+
|
|
19
33
|
## [15.6.1] - 2026-08-19
|
|
20
34
|
|
|
21
35
|
### Changed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mmerterden/multi-agent-pipeline",
|
|
3
|
-
"version": "15.
|
|
3
|
+
"version": "15.7.0",
|
|
4
4
|
"description": "8-phase AI development pipeline with full orchestration on Claude Code, Copilot CLI and Codex CLI. Analysis, planning, TDD, CLI-aware parallel review with consensus surfacing + Fable triage, default-FAIL evidence gates, secret + intent guards, per-phase cost ledger, persistent learnings memory, wiki generation, commit automation. Token-preserving uninstall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -68,6 +68,10 @@ Pipeline (after Phase 0) - shown as visual cards in terminal:
|
|
|
68
68
|
|
|
69
69
|
Autopilot always pauses at the Phase 7 channels menu (30-min timeout → session ends cleanly).
|
|
70
70
|
|
|
71
|
+
--dev pipeline (Phases 1-2 skipped; Review is NEVER skipped):
|
|
72
|
+
Init (same 8 steps), Dev with Opus, Review (same gates + triage),
|
|
73
|
+
Test (skipped in autopilot / --local), Commit + Report (menu still pauses).
|
|
74
|
+
|
|
71
75
|
Every step is logged. Error in any phase -> pause -> resume to continue.
|
|
72
76
|
|
|
73
77
|
------------------------------------------------------------
|
|
@@ -80,7 +84,7 @@ Modes:
|
|
|
80
84
|
autopilot Skip all confirmations INCLUDING plan gate, auto commit/PR
|
|
81
85
|
(EXCEPT Phase 7 channels menu - always pauses)
|
|
82
86
|
--dev autopilot Fastest path
|
|
83
|
-
--local --dev Local + fast (no worktree, no analysis/review
|
|
87
|
+
--local --dev Local + fast (no worktree, no analysis/plan gate - review still runs)
|
|
84
88
|
|
|
85
89
|
Dedicated commands (same as flags above):
|
|
86
90
|
|
|
@@ -213,8 +217,7 @@ Design Check (mock-mode vs Figma, local-only - powered by dev-toolkit-mcp desi
|
|
|
213
217
|
# "Requires a scenario / prefix / launch-arg" is not a reason; reaching those states is the run's job.
|
|
214
218
|
# When a live screen has no confident Figma match, it SHOWS you the screenshot and asks for the node-id.
|
|
215
219
|
|
|
216
|
-
dev-toolkit-mcp design_*
|
|
217
|
-
design_ui_geometry, design_visual_compare, design_report (+ ios_* / android_* device control).
|
|
220
|
+
Drives the dev-toolkit-mcp design_* tool suite (+ ios_* / android_* device control).
|
|
218
221
|
|
|
219
222
|
------------------------------------------------------------
|
|
220
223
|
|
|
@@ -330,13 +333,18 @@ Pipeline (Phase 0'dan sonra) - terminalde görsel kart olarak görünür:
|
|
|
330
333
|
Phase 3: Dev -> TDD: test -> kod -> build (Sonnet) + build queue
|
|
331
334
|
Phase 4: Review -> Deterministik kapılar + paralel AI review + Fable triage
|
|
332
335
|
(Claude Code: Fable + Sonnet · Copilot CLI: GPT-5.4 + Opus + Sonnet)
|
|
333
|
-
Phase 5: Test -> Opsiyonel: branch'e geç, Xcode'da test
|
|
336
|
+
Phase 5: Test -> Opsiyonel: branch'e geç, Xcode'da test
|
|
337
|
+
(dev + full'de koşar; her autopilot ve local varyantında skip)
|
|
334
338
|
Phase 6: Commit -> Commit -> push -> PR + issue body güncelleme (hiç auto-close yok)
|
|
335
339
|
Phase 7: Report -> Channels dispatcher (PR · Jira · Confluence · Wiki, multi-select)
|
|
336
340
|
+ internal capture (agent-log · knowledge · memory)
|
|
337
341
|
|
|
338
342
|
Autopilot Phase 7'deki channels menüsünde HER ZAMAN durur (30 dk timeout → session temiz biter).
|
|
339
343
|
|
|
344
|
+
--dev pipeline'ı (Faz 1-2 atlanır; Review ASLA atlanmaz):
|
|
345
|
+
Init (aynı 8 adım), Dev Opus ile, Review (aynı kapılar + triage),
|
|
346
|
+
Test (autopilot / --local'de atlanır), Commit + Report (menü yine durur).
|
|
347
|
+
|
|
340
348
|
Her adım loglanır. Herhangi bir fazdaki hata -> pause -> resume ile devam et.
|
|
341
349
|
|
|
342
350
|
------------------------------------------------------------
|
|
@@ -349,7 +357,7 @@ Modlar:
|
|
|
349
357
|
autopilot Plan gate dahil tüm onayları atla, otomatik commit/PR
|
|
350
358
|
(İSTİSNA: Phase 7 channels menüsü - her zaman durur)
|
|
351
359
|
--dev autopilot En hızlı yol
|
|
352
|
-
--local --dev Local + hızlı (worktree yok, analiz/
|
|
360
|
+
--local --dev Local + hızlı (worktree yok, analiz/plan gate yok - review yine koşar)
|
|
353
361
|
|
|
354
362
|
Dedicated komutlar (yukarıdaki flag'lerin eşdeğeri):
|
|
355
363
|
|
|
@@ -483,8 +491,7 @@ Design Check (mock-mod vs Figma, yalnızca lokal - dev-toolkit-mcp design_* to
|
|
|
483
491
|
# "Senaryo / prefix / launch-arg gerektirir" gerekçe değildir; o state'lere ulaşmak koşunun işidir.
|
|
484
492
|
# Canlı ekranın Figma karşılığı kesin bulunamazsa SANA screenshot'ı gösterip node-id sorar.
|
|
485
493
|
|
|
486
|
-
|
|
487
|
-
design_ui_geometry, design_visual_compare, design_report (+ ios_* / android_* cihaz kontrolü).
|
|
494
|
+
dev-toolkit-mcp design_* tool takımını sürer (+ ios_* / android_* cihaz kontrolü).
|
|
488
495
|
|
|
489
496
|
------------------------------------------------------------
|
|
490
497
|
|
|
@@ -247,7 +247,7 @@ Security notes to surface to the user: the token is **write-only** (append-only
|
|
|
247
247
|
|
|
248
248
|
### Step 3 - Interactive token onboarding (for missing tokens)
|
|
249
249
|
|
|
250
|
-
Git identities are NOT asked as a separate step. They are learned when tokens are added
|
|
250
|
+
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:
|
|
251
251
|
|
|
252
252
|
```
|
|
253
253
|
Token saved. Which git identity uses this token?
|
|
@@ -269,6 +269,30 @@ Token saved. Which git identity uses this token?
|
|
|
269
269
|
|
|
270
270
|
For each MISSING service, run the **Token Save Flow** below. When creating new tokens, use the **standard key name** from Step 1b table.
|
|
271
271
|
|
|
272
|
+
#### Sequential onboarding loop (strict)
|
|
273
|
+
|
|
274
|
+
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:
|
|
275
|
+
|
|
276
|
+
1. `jira`
|
|
277
|
+
2. `bitbucket_token` (then `bitbucket_user` right after, if missing)
|
|
278
|
+
3. `github`
|
|
279
|
+
4. `confluence`
|
|
280
|
+
5. `figma` - Figma Personal Access Token (Tier 2, REST)
|
|
281
|
+
6. `figma_mcp` - Figma MCP auth (Tier 1); its own prompt, never folded into the PAT prompt
|
|
282
|
+
7. `fortify`
|
|
283
|
+
8. `graylog`
|
|
284
|
+
9. `firebase`
|
|
285
|
+
10. `jenkins`
|
|
286
|
+
11. App Store Connect block (Step 3b; only when the target repo is iOS)
|
|
287
|
+
|
|
288
|
+
Loop rules:
|
|
289
|
+
|
|
290
|
+
- **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.
|
|
291
|
+
- **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). 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.
|
|
292
|
+
- **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.
|
|
293
|
+
- **`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.
|
|
294
|
+
- The loop ends only when every missing service has been asked exactly once; then continue to Step 4 (Verify all).
|
|
295
|
+
|
|
272
296
|
---
|
|
273
297
|
|
|
274
298
|
### Token Save Flow (reusable)
|
|
@@ -424,6 +448,23 @@ Default Jira project key (e.g. PROJ): ___
|
|
|
424
448
|
|
|
425
449
|
Save to `prefs.global.defaultJiraKey`. From Jira host, derive `corpDomain` (e.g. `jira.example.com` → `example.com`) and pre-fill it so the Confluence / Bitbucket prompts only need the subdomain.
|
|
426
450
|
|
|
451
|
+
`defaultJiraKey` stays a single global fallback, but one Jira host usually serves several repos with DIFFERENT project keys, so the Jira pass continues with a per-repo key mapping (multi-select, optional):
|
|
452
|
+
|
|
453
|
+
```
|
|
454
|
+
Per-repo Jira keys (optional) - repos whose project key differs from {defaultJiraKey}:
|
|
455
|
+
|
|
456
|
+
[ ] 1. my-ios-app (current: PROJA)
|
|
457
|
+
[ ] 2. my-other-app (current: - )
|
|
458
|
+
...
|
|
459
|
+
|
|
460
|
+
Toggle repos, then enter the project key for each selected repo. Enter to skip.
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
- 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.
|
|
464
|
+
- 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.
|
|
465
|
+
- 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.
|
|
466
|
+
- **Re-run / update**: `/multi-agent:setup jira-keys` re-opens only this mapping without touching tokens or hosts.
|
|
467
|
+
|
|
427
468
|
Resulting shape:
|
|
428
469
|
|
|
429
470
|
```json
|
|
@@ -512,11 +553,8 @@ If still missing, show which ones and let user skip (pipeline will re-ask for an
|
|
|
512
553
|
|
|
513
554
|
### Step 5 - Repo Discovery (optional)
|
|
514
555
|
|
|
515
|
-
Pre-populates the Phase 0 project picker.
|
|
516
|
-
|
|
517
|
-
Three sources merge into one flat list. Each source is independent - any one failing does NOT block the others.
|
|
556
|
+
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.
|
|
518
557
|
|
|
519
|
-
**Prompt:**
|
|
520
558
|
```
|
|
521
559
|
Step 5 - Repo Discovery (optional)
|
|
522
560
|
Scan for recent repos now to pre-populate the Phase 0 picker?
|
|
@@ -526,103 +564,7 @@ Scan for recent repos now to pre-populate the Phase 0 picker?
|
|
|
526
564
|
n - skip (Phase 0 will prompt for repo path manually)
|
|
527
565
|
```
|
|
528
566
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
```bash
|
|
532
|
-
find "$HOME" -maxdepth 3 -type d -name ".git" 2>/dev/null | while read gitdir; do
|
|
533
|
-
repo_dir="$(dirname "$gitdir")"
|
|
534
|
-
origin="$(git -C "$repo_dir" config --get remote.origin.url 2>/dev/null)"
|
|
535
|
-
last="$(git -C "$repo_dir" log -1 --format=%cI 2>/dev/null)"
|
|
536
|
-
printf '%s\t%s\t%s\n' "$repo_dir" "$origin" "$last"
|
|
537
|
-
done
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
Auxiliary markers (recorded but not required): `*.xcworkspace`, `*.xcodeproj`, `Podfile`, `Package.swift`, `package.json`, `pom.xml`, `build.gradle`. Their presence tags the repo as iOS/macOS, Cocoapods, SPM, Node, Maven, Gradle in the picker.
|
|
541
|
-
|
|
542
|
-
**7b. Bitbucket scan** - last 90 days active
|
|
543
|
-
|
|
544
|
-
Runs only if `keychainMapping.bitbucket_token` is non-null AND `serviceStatus.bitbucket.ok === true` (or cache older than `settings.serviceStatusCacheSeconds`, re-ping first).
|
|
545
|
-
|
|
546
|
-
Bitbucket Data Center (self-hosted) - the host comes from `prefs.global.bitbucketHost` (or prompts once if unset; NEVER hardcoded):
|
|
547
|
-
```bash
|
|
548
|
-
BB_HOST="$(jq -r '.global.bitbucketHost // empty' "$PREFS_FILE")"
|
|
549
|
-
BB_USER="$(~/.claude/lib/credential-store.sh get "$(jq -r '.global.keychainMapping.bitbucket_user' "$PREFS_FILE")" 2>/dev/null)"
|
|
550
|
-
BB_TOK="$(~/.claude/lib/credential-store.sh get "$(jq -r '.global.keychainMapping.bitbucket_token' "$PREFS_FILE")" 2>/dev/null)"
|
|
551
|
-
CUTOFF_MS=$(( ( $(date +%s) - 7776000 ) * 1000 )) # 90 days
|
|
552
|
-
curl -sfS -u "$BB_USER:$BB_TOK" \
|
|
553
|
-
"https://${BB_HOST}/rest/api/1.0/repos?permission=REPO_WRITE&limit=200" \
|
|
554
|
-
| jq -r --argjson cutoff "$CUTOFF_MS" '
|
|
555
|
-
.values[] | select((.updatedDate // 0) >= $cutoff) |
|
|
556
|
-
{ slug: "\(.project.key)/\(.slug)", clone: (.links.clone[] | select(.name=="http") | .href) }'
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
Bitbucket Cloud - swap to `https://api.bitbucket.org/2.0/repositories/{workspace}?role=member&sort=-updated_on`.
|
|
560
|
-
|
|
561
|
-
**7c. GitHub scan** - owner + collaborator, push in last 90 days
|
|
562
|
-
|
|
563
|
-
Runs only if `keychainMapping.github` is non-null AND `serviceStatus.github.ok === true`.
|
|
564
|
-
|
|
565
|
-
```bash
|
|
566
|
-
GH_TOK="$(~/.claude/lib/credential-store.sh get "$(jq -r '.global.keychainMapping.github' "$PREFS_FILE")" 2>/dev/null)"
|
|
567
|
-
CUTOFF_ISO="$(date -u -v-90d +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d '90 days ago' +%Y-%m-%dT%H:%M:%SZ)"
|
|
568
|
-
# gh CLI path (preferred when available)
|
|
569
|
-
gh api --paginate 'user/repos?affiliation=owner,collaborator&sort=pushed&per_page=100' \
|
|
570
|
-
-q ".[] | select(.pushed_at > \"$CUTOFF_ISO\") | {full: .full_name, clone: .clone_url}"
|
|
571
|
-
# Fallback without gh:
|
|
572
|
-
curl -sfS -H "Authorization: Bearer $GH_TOK" \
|
|
573
|
-
'https://api.github.com/user/repos?affiliation=owner,collaborator&sort=pushed&per_page=100'
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
**7d. Merge by origin URL**
|
|
577
|
-
|
|
578
|
-
Normalize clone URLs (strip `git@`, `https://`, trailing `.git`) to a canonical `host/owner/repo` key. Deduplicate:
|
|
579
|
-
- Local + remote match on canonical key → single entry with `path = <local>`, `platform = <remote source>`.
|
|
580
|
-
- Remote-only match → entry with `path: null` (user clones on first use from Phase 0).
|
|
581
|
-
- Local-only (no remote clone URL matches) → kept as-is, `platform: "local"`.
|
|
582
|
-
|
|
583
|
-
**7e. Multi-select picker**
|
|
584
|
-
|
|
585
|
-
```
|
|
586
|
-
Discovered 24 active repos (local + remote ≤ 90 days):
|
|
587
|
-
|
|
588
|
-
[ ] 1. Booking ~/dev/Booking bitbucket iOS · SPM
|
|
589
|
-
[ ] 2. UIComponents ~/dev/UIComponents bitbucket iOS · SPM
|
|
590
|
-
[ ] 3. multi-agent-pipeline ~/multi-agent-pipeline github Node
|
|
591
|
-
[ ] 4. dotfiles (not cloned) github -
|
|
592
|
-
[ ] 5. backend-services ~/work/backend-services bitbucket Maven
|
|
593
|
-
...
|
|
594
|
-
|
|
595
|
-
Toggle with numbers (e.g. "1 3 4"), 'all', 'none', or Enter to skip.
|
|
596
|
-
Selected repos become defaults in the Phase 0 project picker.
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
**7f. Save to preferences**
|
|
600
|
-
|
|
601
|
-
```json
|
|
602
|
-
{
|
|
603
|
-
"global": {
|
|
604
|
-
"recentProjects": [
|
|
605
|
-
{"path": "~/dev/Booking", "label": "Booking", "lastUsed": "2026-04-15T21:08:00Z"},
|
|
606
|
-
{"path": "~/dev/UIComponents", "label": "UIComponents", "lastUsed": "2026-04-15T21:08:00Z"},
|
|
607
|
-
{"path": "~/multi-agent-pipeline", "label": "multi-agent-pipeline","lastUsed": "2026-04-15T21:08:00Z"}
|
|
608
|
-
],
|
|
609
|
-
"serviceStatus": {
|
|
610
|
-
"bitbucket": {"ok": true, "checkedAt": "2026-04-15T21:08:00Z"},
|
|
611
|
-
"github": {"ok": true, "checkedAt": "2026-04-15T21:08:00Z"}
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
```
|
|
616
|
-
|
|
617
|
-
Entries are objects (not bare strings). The schema `recentProjects.items` is a `oneOf` - both shapes validate, but v2.1.0 writes objects so `lastUsed` + `label` are first-class.
|
|
618
|
-
|
|
619
|
-
**7g. Skip / degrade rules**
|
|
620
|
-
|
|
621
|
-
- Source failure (network, VPN, 401, rate-limit) skips that source; the others still run. Failure is recorded in `serviceStatus.{service} = { ok: false, checkedAt: <now>, reason: <short> }` so Phase 0 can also read that cache.
|
|
622
|
-
- **Report the skip, do not swallow it.** Setup is where the user is actively configuring things, so a source that could not be reached is exactly what they need to hear about, and the reason decides their next move: a 401 means refresh that token, no response at all on a corporate host means connect the VPN, and a missing `global.hosts.<service>` means the token has nowhere to point. Print one line per skipped source with its classification (see `refs/keychain.md` Rule 2) before moving on. A discovery pass that quietly returned three repos out of forty reads as "you only have three repos".
|
|
623
|
-
- If all three sources yield zero hits, Step 5 is a no-op - the picker in Phase 0 falls back to manual path entry.
|
|
624
|
-
- Discovery is **never destructive**: it never modifies git state, never writes outside `PREFS_FILE`.
|
|
625
|
-
- Step 5 is idempotent. Re-running it refreshes `recentProjects` and `serviceStatus` but preserves user-curated entries unless explicitly deselected.
|
|
567
|
+
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.
|
|
626
568
|
|
|
627
569
|
### Step 6 - Remember as Multi-Repo Group (optional)
|
|
628
570
|
|
|
@@ -801,23 +743,33 @@ For maximum stdin hygiene (the secret never lands on argv / shell history), use
|
|
|
801
743
|
pbpaste | ~/.claude/scripts/keychain.py set "<SERVICE_NAME>" -
|
|
802
744
|
```
|
|
803
745
|
|
|
804
|
-
### Figma MCP Setup (
|
|
746
|
+
### Figma MCP Setup (Remote / Local question)
|
|
747
|
+
|
|
748
|
+
The `figma_mcp` pass of the Step 3 loop opens with a mode question BEFORE any token is collected:
|
|
805
749
|
|
|
806
|
-
|
|
750
|
+
```
|
|
751
|
+
Figma MCP mode?
|
|
807
752
|
|
|
808
|
-
|
|
809
|
-
-
|
|
810
|
-
|
|
753
|
+
[1] Remote - mcp.figma.com, OAuth token (figu_) (recommended)
|
|
754
|
+
[2] Local - @anthropic-ai/figma-mcp via npx, uses the Figma PAT
|
|
755
|
+
```
|
|
811
756
|
|
|
812
|
-
**
|
|
757
|
+
**Remote** (`figu_` OAuth token - generate it FOR the current user, never just point at docs):
|
|
758
|
+
- 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).
|
|
759
|
+
- 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.
|
|
760
|
+
- Renewal is silent afterwards via `$HOME/.claude/lib/figma-mcp-refresh.sh` (refresh token). Never echo token values.
|
|
761
|
+
|
|
762
|
+
**Local** (PAT-based):
|
|
763
|
+
- 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`:
|
|
813
764
|
```bash
|
|
814
|
-
#
|
|
765
|
+
# stdin sentinel keeps the value out of shell history
|
|
815
766
|
pbpaste | ~/.claude/scripts/keychain.py set "${USER}_Figma_Access_Token" -
|
|
816
767
|
pbcopy < /dev/null
|
|
817
|
-
|
|
818
|
-
# 2. Add MCP to Claude Code settings.json
|
|
819
|
-
# ~/.claude/settings.json -> mcpServers -> figma
|
|
768
|
+
# then: ~/.claude/settings.json -> mcpServers -> figma
|
|
820
769
|
```
|
|
770
|
+
- If `prefs.global.figmaMcp.localGeneratorScript` is set, run that script instead.
|
|
771
|
+
|
|
772
|
+
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.
|
|
821
773
|
|
|
822
774
|
### Security Rules
|
|
823
775
|
|
|
@@ -850,7 +802,7 @@ To set up multi-agent on a new machine:
|
|
|
850
802
|
/multi-agent "bug description"
|
|
851
803
|
```
|
|
852
804
|
|
|
853
|
-
All tokens are optional -
|
|
805
|
+
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.
|
|
854
806
|
|
|
855
807
|
### Step 8 - Enforcement hook (optional, Claude Code)
|
|
856
808
|
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Setup Step 5 - Repo Discovery (full contract)
|
|
2
|
+
|
|
3
|
+
> Split out of `setup/SKILL.md` (token budget). The SKILL keeps the opt-in prompt and a summary; this file carries the scan commands, merge rules, picker, prefs shapes and skip / degrade rules. Read it when the user opts into repo discovery.
|
|
4
|
+
|
|
5
|
+
Pre-populates the Phase 0 project picker. Without this, every task types the repo path by hand; with this, the picker shows a curated list of repos the user has been active in.
|
|
6
|
+
|
|
7
|
+
Three sources merge into one flat list. Each source is independent - any one failing does NOT block the others.
|
|
8
|
+
|
|
9
|
+
**Prompt:**
|
|
10
|
+
```
|
|
11
|
+
Step 5 - Repo Discovery (optional)
|
|
12
|
+
Scan for recent repos now to pre-populate the Phase 0 picker?
|
|
13
|
+
|
|
14
|
+
y - all three (local + Bitbucket + GitHub, ~10-30s)
|
|
15
|
+
l - local scan only (fastest, no network)
|
|
16
|
+
n - skip (Phase 0 will prompt for repo path manually)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**7a. Local scan** - `$HOME` depth-3, repo markers
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
find "$HOME" -maxdepth 3 -type d -name ".git" 2>/dev/null | while read gitdir; do
|
|
23
|
+
repo_dir="$(dirname "$gitdir")"
|
|
24
|
+
origin="$(git -C "$repo_dir" config --get remote.origin.url 2>/dev/null)"
|
|
25
|
+
last="$(git -C "$repo_dir" log -1 --format=%cI 2>/dev/null)"
|
|
26
|
+
printf '%s\t%s\t%s\n' "$repo_dir" "$origin" "$last"
|
|
27
|
+
done
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Auxiliary markers (recorded but not required): `*.xcworkspace`, `*.xcodeproj`, `Podfile`, `Package.swift`, `package.json`, `pom.xml`, `build.gradle`. Their presence tags the repo as iOS/macOS, Cocoapods, SPM, Node, Maven, Gradle in the picker.
|
|
31
|
+
|
|
32
|
+
**7b. Bitbucket scan** - last 90 days active
|
|
33
|
+
|
|
34
|
+
Runs only if `keychainMapping.bitbucket_token` is non-null AND `serviceStatus.bitbucket.ok === true` (or cache older than `settings.serviceStatusCacheSeconds`, re-ping first).
|
|
35
|
+
|
|
36
|
+
Bitbucket Data Center (self-hosted) - the host comes from `prefs.global.bitbucketHost` (or prompts once if unset; NEVER hardcoded):
|
|
37
|
+
```bash
|
|
38
|
+
BB_HOST="$(jq -r '.global.bitbucketHost // empty' "$PREFS_FILE")"
|
|
39
|
+
BB_USER="$(~/.claude/lib/credential-store.sh get "$(jq -r '.global.keychainMapping.bitbucket_user' "$PREFS_FILE")" 2>/dev/null)"
|
|
40
|
+
BB_TOK="$(~/.claude/lib/credential-store.sh get "$(jq -r '.global.keychainMapping.bitbucket_token' "$PREFS_FILE")" 2>/dev/null)"
|
|
41
|
+
CUTOFF_MS=$(( ( $(date +%s) - 7776000 ) * 1000 )) # 90 days
|
|
42
|
+
curl -sfS -u "$BB_USER:$BB_TOK" \
|
|
43
|
+
"https://${BB_HOST}/rest/api/1.0/repos?permission=REPO_WRITE&limit=200" \
|
|
44
|
+
| jq -r --argjson cutoff "$CUTOFF_MS" '
|
|
45
|
+
.values[] | select((.updatedDate // 0) >= $cutoff) |
|
|
46
|
+
{ slug: "\(.project.key)/\(.slug)", clone: (.links.clone[] | select(.name=="http") | .href) }'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Bitbucket Cloud - swap to `https://api.bitbucket.org/2.0/repositories/{workspace}?role=member&sort=-updated_on`.
|
|
50
|
+
|
|
51
|
+
**7c. GitHub scan** - owner + collaborator, push in last 90 days
|
|
52
|
+
|
|
53
|
+
Runs only if `keychainMapping.github` is non-null AND `serviceStatus.github.ok === true`.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
GH_TOK="$(~/.claude/lib/credential-store.sh get "$(jq -r '.global.keychainMapping.github' "$PREFS_FILE")" 2>/dev/null)"
|
|
57
|
+
CUTOFF_ISO="$(date -u -v-90d +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d '90 days ago' +%Y-%m-%dT%H:%M:%SZ)"
|
|
58
|
+
# gh CLI path (preferred when available)
|
|
59
|
+
gh api --paginate 'user/repos?affiliation=owner,collaborator&sort=pushed&per_page=100' \
|
|
60
|
+
-q ".[] | select(.pushed_at > \"$CUTOFF_ISO\") | {full: .full_name, clone: .clone_url}"
|
|
61
|
+
# Fallback without gh:
|
|
62
|
+
curl -sfS -H "Authorization: Bearer $GH_TOK" \
|
|
63
|
+
'https://api.github.com/user/repos?affiliation=owner,collaborator&sort=pushed&per_page=100'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**7d. Merge by origin URL**
|
|
67
|
+
|
|
68
|
+
Normalize clone URLs (strip `git@`, `https://`, trailing `.git`) to a canonical `host/owner/repo` key. Deduplicate:
|
|
69
|
+
- Local + remote match on canonical key → single entry with `path = <local>`, `platform = <remote source>`.
|
|
70
|
+
- Remote-only match → entry with `path: null` (user clones on first use from Phase 0).
|
|
71
|
+
- Local-only (no remote clone URL matches) → kept as-is, `platform: "local"`.
|
|
72
|
+
|
|
73
|
+
**7e. Multi-select picker**
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
Discovered 24 active repos (local + remote ≤ 90 days):
|
|
77
|
+
|
|
78
|
+
[ ] 1. Booking ~/dev/Booking bitbucket iOS · SPM
|
|
79
|
+
[ ] 2. UIComponents ~/dev/UIComponents bitbucket iOS · SPM
|
|
80
|
+
[ ] 3. multi-agent-pipeline ~/multi-agent-pipeline github Node
|
|
81
|
+
[ ] 4. dotfiles (not cloned) github -
|
|
82
|
+
[ ] 5. backend-services ~/work/backend-services bitbucket Maven
|
|
83
|
+
...
|
|
84
|
+
|
|
85
|
+
Toggle with numbers (e.g. "1 3 4"), 'all', 'none', or Enter to skip.
|
|
86
|
+
Selected repos become defaults in the Phase 0 project picker.
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**7f. Save to preferences**
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"global": {
|
|
94
|
+
"recentProjects": [
|
|
95
|
+
{"path": "~/dev/Booking", "label": "Booking", "lastUsed": "2026-04-15T21:08:00Z"},
|
|
96
|
+
{"path": "~/dev/UIComponents", "label": "UIComponents", "lastUsed": "2026-04-15T21:08:00Z"},
|
|
97
|
+
{"path": "~/multi-agent-pipeline", "label": "multi-agent-pipeline","lastUsed": "2026-04-15T21:08:00Z"}
|
|
98
|
+
],
|
|
99
|
+
"serviceStatus": {
|
|
100
|
+
"bitbucket": {"ok": true, "checkedAt": "2026-04-15T21:08:00Z"},
|
|
101
|
+
"github": {"ok": true, "checkedAt": "2026-04-15T21:08:00Z"}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Entries are objects (not bare strings). The schema `recentProjects.items` is a `oneOf` - both shapes validate, but v2.1.0 writes objects so `lastUsed` + `label` are first-class.
|
|
108
|
+
|
|
109
|
+
**7g. Skip / degrade rules**
|
|
110
|
+
|
|
111
|
+
- Source failure (network, VPN, 401, rate-limit) skips that source; the others still run. Failure is recorded in `serviceStatus.{service} = { ok: false, checkedAt: <now>, reason: <short> }` so Phase 0 can also read that cache.
|
|
112
|
+
- **Report the skip, do not swallow it.** Setup is where the user is actively configuring things, so a source that could not be reached is exactly what they need to hear about, and the reason decides their next move: a 401 means refresh that token, no response at all on a corporate host means connect the VPN, and a missing `global.hosts.<service>` means the token has nowhere to point. Print one line per skipped source with its classification (see `refs/keychain.md` Rule 2) before moving on. A discovery pass that quietly returned three repos out of forty reads as "you only have three repos".
|
|
113
|
+
- If all three sources yield zero hits, Step 5 is a no-op - the picker in Phase 0 falls back to manual path entry.
|
|
114
|
+
- Discovery is **never destructive**: it never modifies git state, never writes outside `PREFS_FILE`.
|
|
115
|
+
- Step 5 is idempotent. Re-running it refreshes `recentProjects` and `serviceStatus` but preserves user-curated entries unless explicitly deselected.
|
|
116
|
+
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"keychainMapping": {
|
|
124
124
|
"type": "object",
|
|
125
125
|
"additionalProperties": false,
|
|
126
|
-
"description": "Maps logical token names to actual Keychain item names. Pipeline looks up tokens via `security find-generic-password -s <mapped-name>` (or `pipeline/lib/credential-store.sh get` cross-platform). Covered keys: jira, bitbucket, bitbucket_token, bitbucket_user, github, confluence, figma, figma_mcp, fortify, graylog, firebase, jenkins, npm, claude_oauth_token, claude_oauth_token_fallback. Deprecated and accepted only for backward compatibility: figma_pat (superseded by figma), figma_user (unused).",
|
|
126
|
+
"description": "Maps logical token names to actual Keychain item names. Pipeline looks up tokens via `security find-generic-password -s <mapped-name>` (or `pipeline/lib/credential-store.sh get` cross-platform). Covered keys: jira, bitbucket, bitbucket_token, bitbucket_user, github, confluence, figma, figma_mcp, fortify, graylog, firebase, jenkins, npm, claude_oauth_token, claude_oauth_token_fallback, supabase_access, supabase_service_role. Deprecated and accepted only for backward compatibility: figma_pat (superseded by figma), figma_user (unused).",
|
|
127
127
|
"properties": {
|
|
128
128
|
"jira": {
|
|
129
129
|
"type": [
|
|
@@ -188,6 +188,20 @@
|
|
|
188
188
|
"null"
|
|
189
189
|
]
|
|
190
190
|
},
|
|
191
|
+
"supabase_access": {
|
|
192
|
+
"type": [
|
|
193
|
+
"string",
|
|
194
|
+
"null"
|
|
195
|
+
],
|
|
196
|
+
"description": "Supabase personal access token (Management API). Used by personal-site tooling; not consumed by pipeline phases."
|
|
197
|
+
},
|
|
198
|
+
"supabase_service_role": {
|
|
199
|
+
"type": [
|
|
200
|
+
"string",
|
|
201
|
+
"null"
|
|
202
|
+
],
|
|
203
|
+
"description": "Supabase service_role key for the personal-site project. Server-side only; never shipped to a client."
|
|
204
|
+
},
|
|
191
205
|
"usage_ingest": {
|
|
192
206
|
"type": [
|
|
193
207
|
"string",
|
|
@@ -621,6 +635,27 @@
|
|
|
621
635
|
],
|
|
622
636
|
"description": "Default Jira project key used for placeholder replacement. null until /multi-agent:setup onboards a Jira project."
|
|
623
637
|
},
|
|
638
|
+
"figmaMcp": {
|
|
639
|
+
"type": "object",
|
|
640
|
+
"additionalProperties": false,
|
|
641
|
+
"properties": {
|
|
642
|
+
"remoteGeneratorScript": {
|
|
643
|
+
"type": [
|
|
644
|
+
"string",
|
|
645
|
+
"null"
|
|
646
|
+
],
|
|
647
|
+
"description": "Local-only path to a script that generates the remote MCP OAuth token (figu_) via Dynamic Client Registration + PKCE and saves it to the credential store. Run by the setup figma_mcp pass when the user picks Remote. Personal paths stay in prefs; never embedded in synced files."
|
|
648
|
+
},
|
|
649
|
+
"localGeneratorScript": {
|
|
650
|
+
"type": [
|
|
651
|
+
"string",
|
|
652
|
+
"null"
|
|
653
|
+
],
|
|
654
|
+
"description": "Local-only path to a script that validates the Figma PAT and wires the PAT-based local MCP server (@anthropic-ai/figma-mcp) into Claude Code settings.json. Run by the setup figma_mcp pass when the user picks Local."
|
|
655
|
+
}
|
|
656
|
+
},
|
|
657
|
+
"description": "Figma MCP onboarding helpers for /multi-agent:setup. Both fields optional; when unset the setup flow guides the token generation inline."
|
|
658
|
+
},
|
|
624
659
|
"modelFallback": {
|
|
625
660
|
"type": "object",
|
|
626
661
|
"additionalProperties": false,
|