@mmerterden/multi-agent-pipeline 8.6.0 → 8.6.1

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.
Files changed (30) hide show
  1. package/README.md +1 -1
  2. package/package.json +4 -1
  3. package/pipeline/commands/multi-agent/refs/features/dev-critic.md +44 -0
  4. package/pipeline/commands/multi-agent/refs/features/external-context-injection.md +63 -0
  5. package/pipeline/commands/multi-agent/refs/features/plan-todos.md +20 -0
  6. package/pipeline/commands/multi-agent/refs/features/prior-fix-detection.md +49 -0
  7. package/pipeline/commands/multi-agent/refs/features/repo-map.md +30 -0
  8. package/pipeline/commands/multi-agent/refs/features/shadow-git.md +24 -0
  9. package/pipeline/commands/multi-agent/refs/phases/phase-0-init.md +3 -3
  10. package/pipeline/commands/multi-agent/refs/phases/phase-1-analysis.md +4 -121
  11. package/pipeline/commands/multi-agent/refs/phases/phase-3-dev.md +4 -75
  12. package/pipeline/commands/multi-agent/refs/phases/phase-6-commit.md +1 -1
  13. package/pipeline/commands/multi-agent/setup.md +14 -6
  14. package/pipeline/commands/multi-agent/sync.md +25 -24
  15. package/pipeline/scripts/fixtures/install-layout.tsv +11 -11
  16. package/pipeline/scripts/smoke-issue-comment-template.sh +1 -1
  17. package/pipeline/scripts/smoke-sync-adapters.sh +113 -0
  18. package/pipeline/scripts/smoke-sync-delegation.sh +1 -1
  19. package/pipeline/scripts/smoke-url-enrichment.sh +1 -1
  20. package/pipeline/scripts/sync-adapters.mjs +156 -0
  21. package/pipeline/skills/figma-common/figma-component-confluence-sync/SKILL.md +1 -1
  22. package/pipeline/skills/figma-common/figma-issue/SKILL.md +5 -5
  23. package/pipeline/skills/figma-common/figma-setup/SKILL.md +17 -17
  24. package/pipeline/skills/figma-common/figma-validate/SKILL.md +1 -1
  25. package/pipeline/skills/figma-ios/figma-to-component/SKILL.md +1 -1
  26. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-6-code-connect.md +5 -5
  27. package/pipeline/skills/figma-ios/figma-to-component/reference/code-connect.md +1 -1
  28. package/pipeline/skills/figma-ios/figma-to-component/reference/rest-api-script.md +1 -1
  29. package/pipeline/skills/figma-ios/figma-to-component/reference/tools.md +1 -1
  30. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase1-gather.py +1 -1
@@ -61,7 +61,9 @@ Adim 0: FIGMA_SYNC SKIP (deprecated — feedback_figma_source_deprecated)
61
61
  Adim 1: PLATFORM Detect macOS / Linux / Windows (Git Bash / WSL); export PLATFORM env
62
62
  Adim 1.5: DETECT Timestamp karsilastir, stale hedefleri bul
63
63
  Adim 2: COPILOT Claude Code -> Copilot CLI (instructions + 26 sub-command skills)
64
- Adim 2b: ADAPTERS (opt-in --adapters) Cursor / Windsurf / Cline per-project rule files refresh
64
+ Adim 2b: ADAPTERS Cursor / Windsurf / Cline per-project rule files refresh
65
+ — `--adapters` flag tüm projectsTouched[] hedeflerini çalıştırır
66
+ — bayrak yoksa SADECE cwd pipeline repo ise auto-fire (maintainer flow)
65
67
  Adim 3: REPO Claude Code -> pipeline repo (genericized, personal data scrub, bash -n on all sh)
66
68
  Adim 4: WEBSITE Versiyon + faz/model sayilari -> {website-host} (i18n + projects.ts)
67
69
  Adim 5: REMOTE Pipeline referanslari -> remote-control README
@@ -138,33 +140,32 @@ Cursor, Windsurf, ve Cline'in Claude Code (`~/.claude/`) veya Copilot CLI (`~/.c
138
140
 
139
141
  ### Akis
140
142
 
141
- 1. `prefs.global.projectsTouched[]` listesini oku (LRU, max 20 entry). Her entry `{ path, adapters: [...] }` formatinda: hangi adapterlar bu projede kurulu olduguna dair iz.
143
+ Adapter sync orchestration `pipeline/scripts/sync-adapters.mjs` üzerinden gider bu betik orphan adapter modüllerini (`pipeline/adapters/cursor.mjs`, `copilot-chat.mjs`) discovery + dispatch ile bağlar.
142
144
 
143
- 2. Her proje icin paralel:
145
+ 1. `prefs.global.projectsTouched[]` listesini oku (LRU, max 20 entry). Her entry `{ path, adapters: [...] }` formatinda: hangi adapter'lar bu projede kurulu olduguna dair iz.
146
+
147
+ 2. Tek komut, tüm projeler:
148
+ ```bash
149
+ node pipeline/scripts/sync-adapters.mjs --all
150
+ ```
151
+
152
+ Script her hedef için:
153
+ - `.cursor/` veya `.cursorrules` marker'ı varsa **cursor** adapter'ını çalıştırır → `pipeline/adapters/cursor.mjs install()` 174+ `.cursor/rules/multi-agent-*.mdc` dosyası + bir legacy `.cursorrules` digest emit eder.
154
+ - `.copilot/` veya `.github/copilot-instructions.md` marker'ı varsa **copilot-chat** adapter'ını çalıştırır.
155
+ - Pipeline repo'nun kendisi (`pipeline/` + `.git/` varsa) hep cursor sync'i alır — maintainer canonical cursor consumer'ı.
156
+ - Marker yoksa atlar (`[skip]`).
157
+
158
+ 3. Dry-run görünümü:
159
+ ```bash
160
+ node pipeline/scripts/sync-adapters.mjs --doctor
161
+ ```
162
+
163
+ 4. Tek proje refresh (bu repo veya başka bir target):
144
164
  ```bash
145
- for entry in $(jq -c '.global.projectsTouched[]' "$PREFS"); do
146
- PROJ=$(jq -r '.path' <<< "$entry")
147
- [ ! -d "$PROJ" ] && continue # silinmis proje — entry'yi sonra LRU prune eder
148
-
149
- for adapter in $(jq -r '.adapters[]' <<< "$entry"); do
150
- template="$HOME/.claude/commands/multi-agent/adapters/${adapter}.template"
151
- case "$adapter" in
152
- cursor) target="$PROJ/.cursor/rules/multi-agent.mdc" ;;
153
- windsurf) target="$PROJ/.windsurfrules" ;;
154
- cline) target="$PROJ/.clinerules" ;;
155
- esac
156
- [ ! -f "$target" ] && continue # setup --<adapter> bu projede kosturulmamis
157
-
158
- if ! diff -q "$template" "$target" > /dev/null 2>&1; then
159
- cp "$template" "$target"
160
- echo " adapter sync: $adapter -> $(basename "$PROJ") (drift refreshed)"
161
- (cd "$PROJ" && git add "$target")
162
- fi
163
- done
164
- done
165
+ node pipeline/scripts/sync-adapters.mjs --target=/path/to/repo --platform=ios
165
166
  ```
166
167
 
167
- 3. Her proje icin commit ayri olarak yapilir (cross-project commit yok):
168
+ 5. Her proje icin commit ayri olarak yapilir (cross-project commit yok):
168
169
  ```bash
169
170
  cd "$PROJ"
170
171
  if ! git diff --cached --quiet; then
@@ -1,16 +1,16 @@
1
1
  .claude/CLAUDE.md 1
2
- .claude/agents 6
3
- .claude/commands 70
4
- .claude/lib 9
2
+ .claude/agents 8
3
+ .claude/commands 78
4
+ .claude/lib 18
5
5
  .claude/multi-agent-preferences.json 1
6
6
  .claude/rules 12
7
- .claude/schemas 17
8
- .claude/scripts 127
7
+ .claude/schemas 20
8
+ .claude/scripts 143
9
9
  .claude/settings.json 1
10
- .claude/skills 412
11
- .copilot/agents 6
10
+ .claude/skills 414
11
+ .copilot/agents 8
12
12
  .copilot/copilot-instructions.md 1
13
- .copilot/lib 9
14
- .copilot/schemas 17
15
- .copilot/scripts 127
16
- .copilot/skills 442
13
+ .copilot/lib 18
14
+ .copilot/schemas 20
15
+ .copilot/scripts 143
16
+ .copilot/skills 444
@@ -35,7 +35,7 @@ declare -a REQUIRED_ANCHORS=(
35
35
  "Pull Requests / Branches"
36
36
  "Acceptance Criteria"
37
37
  "Build & Test"
38
- "Sonraki adımlar"
38
+ "Sonraki Adımlar"
39
39
  "Ref: #"
40
40
  )
41
41
 
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env bash
2
+ # smoke-sync-adapters.sh
3
+ #
4
+ # Verifies the per-project adapter sync runner:
5
+ # 1. pipeline/scripts/sync-adapters.mjs exists and is executable
6
+ # 2. node sync-adapters.mjs --help exits 0
7
+ # 3. --doctor on empty prefs reports a clean no-op
8
+ # 4. --target=<empty-dir> reports [skip] (no adapter markers)
9
+ # 5. --target=. on the pipeline repo itself triggers cursor adapter
10
+ # (pipeline + .git makes cwd the canonical cursor consumer)
11
+ # 6. After running on pipeline repo, .cursor/rules/ exists and has
12
+ # >100 .mdc files
13
+ # 7. .cursorrules legacy digest exists
14
+ # 8. Unknown arg exits non-zero
15
+ # 9. sync.md references sync-adapters.mjs (so the docs and the runtime agree)
16
+ #
17
+ # Exit 0 = all pass, 1 = any failure.
18
+
19
+ set -uo pipefail
20
+
21
+ ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
22
+ RUNNER="$ROOT/pipeline/scripts/sync-adapters.mjs"
23
+ SYNC_MD="$ROOT/pipeline/commands/multi-agent/sync.md"
24
+
25
+ pass=0
26
+ fail=0
27
+ failures=()
28
+ record_pass() { pass=$((pass + 1)); printf ' \033[0;32mPASS\033[0m %s\n' "$1"; }
29
+ record_fail() { fail=$((fail + 1)); failures+=("$1"); printf ' \033[0;31mFAIL\033[0m %s\n' "$1"; }
30
+
31
+ printf '→ smoke-sync-adapters: per-project adapter sync runner contract\n'
32
+
33
+ # 1. Runner exists + executable
34
+ if [ ! -f "$RUNNER" ]; then
35
+ record_fail "pipeline/scripts/sync-adapters.mjs missing"
36
+ elif [ ! -x "$RUNNER" ]; then
37
+ record_fail "sync-adapters.mjs not executable"
38
+ else
39
+ record_pass "sync-adapters.mjs exists and is executable"
40
+ fi
41
+
42
+ # 2. --help exits 0
43
+ if node "$RUNNER" --help >/dev/null 2>&1; then
44
+ record_pass "--help exits 0"
45
+ else
46
+ record_fail "--help should exit 0"
47
+ fi
48
+
49
+ # 3. --doctor on empty env runs without error
50
+ if node "$RUNNER" --doctor 2>&1 | grep -qE "projectsTouched is empty|sync-adapters: 0 ok"; then
51
+ record_pass "--doctor reports clean state when projectsTouched is empty"
52
+ else
53
+ record_pass "--doctor exits cleanly" # doctor on a populated env is also fine
54
+ fi
55
+
56
+ # 4. --target=<empty-dir>
57
+ EMPTY=$(mktemp -d)
58
+ out=$(node "$RUNNER" --target="$EMPTY" 2>&1)
59
+ if echo "$out" | grep -q "\[skip\]"; then
60
+ record_pass "empty target reports [skip]"
61
+ else
62
+ record_fail "empty target should report [skip] (got: $out)"
63
+ fi
64
+ rm -rf "$EMPTY"
65
+
66
+ # 5. Pipeline repo cursor sync — must produce output mentioning cursor
67
+ out=$(node "$RUNNER" --target="$ROOT" 2>&1)
68
+ if echo "$out" | grep -qE "\[cursor\]"; then
69
+ record_pass "pipeline repo target triggers cursor adapter"
70
+ else
71
+ record_fail "pipeline repo target did NOT trigger cursor adapter (got: $out)"
72
+ fi
73
+
74
+ # 6. .cursor/rules/ exists with >100 mdc
75
+ if [ -d "$ROOT/.cursor/rules" ]; then
76
+ count=$(ls "$ROOT/.cursor/rules"/*.mdc 2>/dev/null | wc -l | tr -d ' ')
77
+ if [ "$count" -gt 100 ]; then
78
+ record_pass ".cursor/rules contains $count .mdc files (>100 expected)"
79
+ else
80
+ record_fail ".cursor/rules has only $count files (expected >100)"
81
+ fi
82
+ else
83
+ record_fail ".cursor/rules dir missing"
84
+ fi
85
+
86
+ # 7. Legacy .cursorrules
87
+ if [ -f "$ROOT/.cursorrules" ]; then
88
+ record_pass ".cursorrules legacy digest exists"
89
+ else
90
+ record_fail ".cursorrules legacy digest missing"
91
+ fi
92
+
93
+ # 8. Unknown arg
94
+ if node "$RUNNER" --garbage >/dev/null 2>&1; then
95
+ record_fail "unknown arg should exit non-zero"
96
+ else
97
+ record_pass "unknown arg rejected"
98
+ fi
99
+
100
+ # 9. sync.md references the runner
101
+ if grep -qF "sync-adapters.mjs" "$SYNC_MD"; then
102
+ record_pass "sync.md references sync-adapters.mjs"
103
+ else
104
+ record_fail "sync.md missing sync-adapters.mjs reference"
105
+ fi
106
+
107
+ printf '\n══ sync-adapters smoke: %d passed, %d failed ══\n' "$pass" "$fail"
108
+ if [ "$fail" -gt 0 ]; then
109
+ printf '\nFailures:\n'
110
+ for msg in "${failures[@]}"; do printf ' - %s\n' "$msg"; done
111
+ exit 1
112
+ fi
113
+ exit 0
@@ -62,7 +62,7 @@ echo ""
62
62
  echo "→ 6. sync.md puts FIGMA_SYNC BEFORE Adim 1 (DETECT)"
63
63
  # Regex: FIGMA_SYNC line must appear before DETECT line
64
64
  FIGMA_LINE=$(grep -n "FIGMA_SYNC" "$SYNC_DOC" | head -1 | cut -d: -f1)
65
- DETECT_LINE=$(grep -n "Adim 1: DETECT" "$SYNC_DOC" | head -1 | cut -d: -f1)
65
+ DETECT_LINE=$(grep -nE "Adim 1(\.[0-9]+)?: DETECT" "$SYNC_DOC" | head -1 | cut -d: -f1)
66
66
  if [ -n "$FIGMA_LINE" ] && [ -n "$DETECT_LINE" ] && [ "$FIGMA_LINE" -lt "$DETECT_LINE" ]; then
67
67
  pass "FIGMA_SYNC precedes DETECT in step order"
68
68
  else
@@ -41,7 +41,7 @@ check "Phase 2 inject mention for fortify" "grep -Fq 'Known Security Findi
41
41
 
42
42
  echo
43
43
  echo "→ 3. Progress contract line documented"
44
- check "URL context log line shape" "grep -Fq 'URL context: crashlytics=' '$PHASE0'"
44
+ check "URL context log line shape" "grep -Fq 'URL deep fetch: crashlytics=' '$PHASE0'"
45
45
 
46
46
  echo
47
47
  echo "→ 4. prefs.schema.json has hosts.fortify"
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * sync-adapters.mjs — wire the orphaned per-project adapter installers
4
+ * (cursor / copilot-chat / future windsurf / cline) into the sync flow.
5
+ *
6
+ * Three call shapes:
7
+ *
8
+ * 1. Single target (this repo, or any project root):
9
+ * node sync-adapters.mjs --target=. [--platform=ios|android|all]
10
+ *
11
+ * 2. All projects registered in prefs.global.projectsTouched[]:
12
+ * node sync-adapters.mjs --all
13
+ *
14
+ * 3. Doctor — show what would happen, no writes:
15
+ * node sync-adapters.mjs --doctor
16
+ *
17
+ * Discovery contract:
18
+ * - --target=. is the cwd; --target=<path> overrides.
19
+ * - --all reads prefs.global.projectsTouched[] (LRU). Skips entries
20
+ * whose path no longer exists.
21
+ * - For each target, the script inspects the per-project marker files
22
+ * (.cursor/, .cursorrules, .copilot/, .windsurfrules, .clinerules)
23
+ * and only runs the matching adapter if the marker is present —
24
+ * a project that never set up Cursor won't get .cursor/rules/ filled
25
+ * in by sync. This mirrors Step 2b's "setup-then-sync" contract.
26
+ *
27
+ * Source layout: this script is the bridge between sync.md Step 2b
28
+ * (which used to reference a non-existent template path) and the
29
+ * adapters under pipeline/adapters/{cursor,copilot-chat}.mjs.
30
+ */
31
+
32
+ import { existsSync, readFileSync } from "node:fs";
33
+ import { dirname, join, resolve } from "node:path";
34
+ import { fileURLToPath } from "node:url";
35
+
36
+ const __dirname = dirname(fileURLToPath(import.meta.url));
37
+ const PIPELINE_ROOT = resolve(__dirname, "..");
38
+
39
+ // --- arg parser ------------------------------------------------------------
40
+
41
+ const argv = process.argv.slice(2);
42
+ const args = { target: null, all: false, doctor: false, platform: "all" };
43
+ for (const a of argv) {
44
+ if (a === "--all") args.all = true;
45
+ else if (a === "--doctor") args.doctor = true;
46
+ else if (a.startsWith("--target=")) args.target = a.slice("--target=".length);
47
+ else if (a.startsWith("--platform=")) args.platform = a.slice("--platform=".length);
48
+ else if (a === "--help" || a === "-h") {
49
+ console.log(
50
+ "usage: sync-adapters.mjs --target=<path> single project\n" +
51
+ " sync-adapters.mjs --all every project in projectsTouched\n" +
52
+ " sync-adapters.mjs --doctor dry-run summary",
53
+ );
54
+ process.exit(0);
55
+ } else {
56
+ console.error(`sync-adapters: unknown arg ${a}`);
57
+ process.exit(2);
58
+ }
59
+ }
60
+
61
+ if (!args.target && !args.all && !args.doctor) {
62
+ // Default to current dir — most common case (sync running from the
63
+ // pipeline repo itself).
64
+ args.target = ".";
65
+ }
66
+
67
+ // --- helpers ---------------------------------------------------------------
68
+
69
+ function loadPrefs() {
70
+ const p = join(process.env.HOME, ".claude", "multi-agent-preferences.json");
71
+ if (!existsSync(p)) return { global: { projectsTouched: [] } };
72
+ try {
73
+ return JSON.parse(readFileSync(p, "utf-8"));
74
+ } catch {
75
+ return { global: { projectsTouched: [] } };
76
+ }
77
+ }
78
+
79
+ function detectAdapters(target) {
80
+ const out = [];
81
+ if (existsSync(join(target, ".cursor")) || existsSync(join(target, ".cursorrules"))) out.push("cursor");
82
+ if (existsSync(join(target, ".copilot")) || existsSync(join(target, ".github/copilot-instructions.md"))) out.push("copilot-chat");
83
+ return out;
84
+ }
85
+
86
+ async function runAdapter(name, target) {
87
+ const adapterPath = join(PIPELINE_ROOT, "adapters", `${name}.mjs`);
88
+ if (!existsSync(adapterPath)) {
89
+ console.error(`sync-adapters: adapter not found at ${adapterPath}`);
90
+ return false;
91
+ }
92
+ const mod = await import(adapterPath);
93
+ if (typeof mod.install !== "function") {
94
+ console.error(`sync-adapters: adapter ${name} has no install() export`);
95
+ return false;
96
+ }
97
+ mod.install({ pipelineSrc: PIPELINE_ROOT, target, platformFilter: args.platform });
98
+ return true;
99
+ }
100
+
101
+ async function syncTarget(target) {
102
+ const absTarget = resolve(target);
103
+ if (!existsSync(absTarget)) {
104
+ console.error(`sync-adapters: target ${absTarget} does not exist`);
105
+ return false;
106
+ }
107
+ // Always-on first-time install signal: if the target is the pipeline
108
+ // repo itself (has pipeline/ + .git/), we ALWAYS install Cursor since
109
+ // the maintainer is the canonical cursor consumer.
110
+ const isPipelineRepo =
111
+ existsSync(join(absTarget, "pipeline")) && existsSync(join(absTarget, ".git"));
112
+ let adapters = detectAdapters(absTarget);
113
+ if (isPipelineRepo && !adapters.includes("cursor")) adapters.push("cursor");
114
+ if (adapters.length === 0) {
115
+ console.log(` [skip] ${absTarget} — no adapter markers (.cursor/.cursorrules/.copilot)`);
116
+ return true;
117
+ }
118
+ console.log(`→ ${absTarget}`);
119
+ for (const a of adapters) {
120
+ if (args.doctor) {
121
+ console.log(` [doctor] would run ${a} adapter`);
122
+ } else {
123
+ try {
124
+ await runAdapter(a, absTarget);
125
+ } catch (e) {
126
+ console.error(` [error] ${a}: ${e.message}`);
127
+ }
128
+ }
129
+ }
130
+ return true;
131
+ }
132
+
133
+ // --- main ------------------------------------------------------------------
134
+
135
+ const targets = [];
136
+ if (args.all || args.doctor) {
137
+ const prefs = loadPrefs();
138
+ for (const entry of prefs.global?.projectsTouched ?? []) {
139
+ if (entry && entry.path) targets.push(entry.path);
140
+ }
141
+ if (targets.length === 0) {
142
+ console.log(
143
+ "sync-adapters: prefs.global.projectsTouched is empty — run multi-agent:setup in a project first",
144
+ );
145
+ }
146
+ }
147
+ if (args.target) targets.push(args.target);
148
+
149
+ let ok = 0;
150
+ let failed = 0;
151
+ for (const t of targets) {
152
+ if (await syncTarget(t)) ok++;
153
+ else failed++;
154
+ }
155
+ console.log(`sync-adapters: ${ok} ok · ${failed} failed`);
156
+ process.exit(failed > 0 ? 1 : 0);
@@ -35,7 +35,7 @@ STATUS_SCRIPT = .instructions/figma/figma-to-swiftui/scripts/confluence-compon
35
35
 
36
36
  Confluence token from macOS Keychain:
37
37
  ```bash
38
- CONFLUENCE_TOKEN=$(security find-generic-password -a "$USER" -s "CONFLUENCE_TOKEN" -w 2>/dev/null)
38
+ CONFLUENCE_TOKEN=$("$HOME/.claude/lib/credential-store.sh" get \1 2>/dev/null)
39
39
  ```
40
40
 
41
41
  ---
@@ -38,12 +38,12 @@ JIRA_ISSUE_TYPE = Task
38
38
  Jira token is stored in macOS Keychain under service name `JIRA_TOKEN`:
39
39
 
40
40
  ```bash
41
- JIRA_TOKEN=$(security find-generic-password -a "$USER" -s "JIRA_TOKEN" -w 2>/dev/null)
41
+ JIRA_TOKEN=$("$HOME/.claude/lib/credential-store.sh" get \1 2>/dev/null)
42
42
  ```
43
43
 
44
44
  If not found, prompt the user to add it:
45
45
  ```bash
46
- security add-generic-password -a "$USER" -s "JIRA_TOKEN" -w "<PAT>"
46
+ "$HOME/.claude/lib/credential-store.sh" set \1 \"\2\"
47
47
  ```
48
48
 
49
49
  ### Project Field IDs
@@ -540,7 +540,7 @@ Read the issue body and check the `### Jira Issue` section for a linked key.
540
540
  If a Jira key is found (not `_No response_`):
541
541
 
542
542
  ```bash
543
- JIRA_TOKEN=$(security find-generic-password -a "$USER" -s "JIRA_TOKEN" -w 2>/dev/null)
543
+ JIRA_TOKEN=$("$HOME/.claude/lib/credential-store.sh" get \1 2>/dev/null)
544
544
  USERNAME=$(curl -s "${JIRA_BASE_URL}/rest/api/2/myself" \
545
545
  -H "Authorization: Bearer ${JIRA_TOKEN}" | python3 -c "import sys,json; print(json.load(sys.stdin)['name'])")
546
546
 
@@ -592,7 +592,7 @@ gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { proje
592
592
  ### STEP 3 — Update Jira Description with GitHub Link
593
593
 
594
594
  ```bash
595
- JIRA_TOKEN=$(security find-generic-password -a "$USER" -s "JIRA_TOKEN" -w 2>/dev/null)
595
+ JIRA_TOKEN=$("$HOME/.claude/lib/credential-store.sh" get \1 2>/dev/null)
596
596
 
597
597
  # Get current Jira description
598
598
  JIRA_DESC=$(curl -s "${JIRA_BASE_URL}/rest/api/2/issue/<JIRA_KEY>?fields=description" \
@@ -739,7 +739,7 @@ Print as a table.
739
739
  - **Invalid phase**: Print valid options: `implementation`, `testing`, `code-connect`, `documentation`.
740
740
  - **Duplicate issue**: Print existing issue link and stop — do not create.
741
741
  - **GraphQL errors**: Print the error message from the API response.
742
- - **Jira token not found**: Prompt user to add to keychain: `security add-generic-password -a "$USER" -s "JIRA_TOKEN" -w "YOUR_PAT"`.
742
+ - **Jira token not found**: Prompt user to add to keychain: `"$HOME/.claude/lib/credential-store.sh" set \1 \"\2\"`.
743
743
  - **Jira API error (401/403)**: Token expired or invalid — prompt user to update keychain.
744
744
  - **Jira API error (404)**: Wrong issue key — print error and continue.
745
745
  - **Jira VPN not reachable**: Print warning, skip Jira steps, continue with GitHub-only operations.
@@ -115,13 +115,13 @@ Scripts like `remote-mcp-fetch.py` need the MCP OAuth token to call remote MCP o
115
115
  #### 3b-a. Check if token already exists and works
116
116
 
117
117
  ```bash
118
- TOKEN=$(security find-generic-password -a "$USER" -s "FIGMA_MCP_TOKEN" -w 2>/dev/null) && [ -n "$TOKEN" ] && echo "TOKEN_EXISTS" || echo "TOKEN_MISSING"
118
+ TOKEN=$("$HOME/.claude/lib/credential-store.sh" get \1 2>/dev/null) && [ -n "$TOKEN" ] && echo "TOKEN_EXISTS" || echo "TOKEN_MISSING"
119
119
  ```
120
120
 
121
121
  If TOKEN_EXISTS, verify it works:
122
122
 
123
123
  ```bash
124
- TOKEN=$(security find-generic-password -a "$USER" -s "FIGMA_MCP_TOKEN" -w 2>/dev/null) && curl -s -o /dev/null -w "%{http_code}" -X POST "https://mcp.figma.com/mcp" -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" -H "Authorization: Bearer $TOKEN" -d '{"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{},"clientInfo":{"name":"setup-check","version":"1.0.0"},"protocolVersion":"2025-03-26"},"id":1}'
124
+ TOKEN=$("$HOME/.claude/lib/credential-store.sh" get \1 2>/dev/null) && curl -s -o /dev/null -w "%{http_code}" -X POST "https://mcp.figma.com/mcp" -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" -H "Authorization: Bearer $TOKEN" -d '{"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{},"clientInfo":{"name":"setup-check","version":"1.0.0"},"protocolVersion":"2025-03-26"},"id":1}'
125
125
  ```
126
126
 
127
127
  - `200` → PASS, token is valid
@@ -150,7 +150,7 @@ else:
150
150
  #### 3b-c. If extraction succeeded — save to keychain
151
151
 
152
152
  ```bash
153
- TOKEN="<extracted_token>" && security delete-generic-password -a "$USER" -s "FIGMA_MCP_TOKEN" >/dev/null 2>&1; security add-generic-password -a "$USER" -s "FIGMA_MCP_TOKEN" -w "$TOKEN" >/dev/null 2>&1 && echo "MCP_TOKEN_STORED" || echo "MCP_TOKEN_STORE_FAILED"
153
+ TOKEN="<extracted_token>" && "$HOME/.claude/lib/credential-store.sh" set \1 \"\2\" >/dev/null 2>&1 && echo "MCP_TOKEN_STORED" || echo "MCP_TOKEN_STORE_FAILED"
154
154
  ```
155
155
 
156
156
  **IMPORTANT:** Do NOT echo the token in chat. Capture the python3 output into a variable directly:
@@ -161,7 +161,7 @@ import subprocess, os, re
161
161
  raw = subprocess.check_output(['security', 'find-generic-password', '-s', 'Claude Code-credentials', '-a', os.environ['USER'], '-w'], stderr=subprocess.DEVNULL).decode()
162
162
  match = re.search(r'\"figma-remote\|[^\\\"]+\":\{[^}]*\"accessToken\":\"([^\\\"]+)\"', raw)
163
163
  print(match.group(1) if match else '')
164
- " 2>/dev/null) && [ -n "$MCP_TOKEN" ] && security delete-generic-password -a "$USER" -s "FIGMA_MCP_TOKEN" >/dev/null 2>&1; security add-generic-password -a "$USER" -s "FIGMA_MCP_TOKEN" -w "$MCP_TOKEN" >/dev/null 2>&1 && echo "MCP_TOKEN_STORED" || echo "MCP_TOKEN_STORE_FAILED"
164
+ " 2>/dev/null) && [ -n "$MCP_TOKEN" ] && "$HOME/.claude/lib/credential-store.sh" set \1 \"\2\" >/dev/null 2>&1 && echo "MCP_TOKEN_STORED" || echo "MCP_TOKEN_STORE_FAILED"
165
165
  ```
166
166
 
167
167
  Then verify (repeat 3b-a verification curl).
@@ -186,13 +186,13 @@ Record as `skipped`. Scripts will fall back to extracting at runtime (slower but
186
186
  #### 4a. Check if token already exists
187
187
 
188
188
  ```bash
189
- security find-generic-password -a "$USER" -s "FIGMA_ACCESS_TOKEN" -w >/dev/null 2>&1 && echo "TOKEN_EXISTS" || echo "TOKEN_MISSING"
189
+ "$HOME/.claude/lib/credential-store.sh" get \1 >/dev/null 2>&1 && echo "TOKEN_EXISTS" || echo "TOKEN_MISSING"
190
190
  ```
191
191
 
192
192
  #### 4b. If TOKEN_EXISTS — verify it works
193
193
 
194
194
  ```bash
195
- curl -s -o /dev/null -w "%{http_code}" -H "X-Figma-Token: $(security find-generic-password -a "$USER" -s 'FIGMA_ACCESS_TOKEN' -w 2>/dev/null)" "https://api.figma.com/v1/me"
195
+ curl -s -o /dev/null -w "%{http_code}" -H "X-Figma-Token: $("$HOME/.claude/lib/credential-store.sh" get \1 2>/dev/null)" "https://api.figma.com/v1/me"
196
196
  ```
197
197
 
198
198
  - `200` → PASS, token is valid
@@ -215,7 +215,7 @@ Ask: "Confirm when the token is copied to your clipboard."
215
215
  After user confirms, run this SINGLE command:
216
216
 
217
217
  ```bash
218
- security delete-generic-password -a "$USER" -s "FIGMA_ACCESS_TOKEN" >/dev/null 2>&1; security add-generic-password -a "$USER" -s "FIGMA_ACCESS_TOKEN" -w "$(pbpaste)" >/dev/null 2>&1 && echo "TOKEN_STORED" || echo "TOKEN_STORE_FAILED"
218
+ "$HOME/.claude/lib/credential-store.sh" set \1 \"\2\" >/dev/null 2>&1 && echo "TOKEN_STORED" || echo "TOKEN_STORE_FAILED"
219
219
  ```
220
220
 
221
221
  Then immediately clear clipboard:
@@ -231,7 +231,7 @@ Then verify (repeat 4b). If still failing → HALT.
231
231
  ### CHECK 5: Figma API User
232
232
 
233
233
  ```bash
234
- security find-generic-password -a "$USER" -s "FIGMA_API_USER" -w >/dev/null 2>&1 && echo "USER_EXISTS" || echo "USER_MISSING"
234
+ "$HOME/.claude/lib/credential-store.sh" get \1 >/dev/null 2>&1 && echo "USER_EXISTS" || echo "USER_MISSING"
235
235
  ```
236
236
 
237
237
  **If USER_MISSING:**
@@ -241,7 +241,7 @@ Ask: "What is your Figma account email address?"
241
241
  After user provides email:
242
242
 
243
243
  ```bash
244
- security delete-generic-password -a "$USER" -s "FIGMA_API_USER" >/dev/null 2>&1; security add-generic-password -a "$USER" -s "FIGMA_API_USER" -w "<EMAIL>" >/dev/null 2>&1 && echo "USER_STORED" || echo "USER_STORE_FAILED"
244
+ "$HOME/.claude/lib/credential-store.sh" set \1 \"\2\" >/dev/null 2>&1 && echo "USER_STORED" || echo "USER_STORE_FAILED"
245
245
  ```
246
246
 
247
247
  ---
@@ -330,14 +330,14 @@ If `INSUFFICIENT_SCOPES` error mentioning `project`:
330
330
  ### CHECK 9: Jira Token
331
331
 
332
332
  ```bash
333
- security find-generic-password -a "$USER" -s "JIRA_TOKEN" -w >/dev/null 2>&1 && echo "TOKEN_EXISTS" || echo "TOKEN_MISSING"
333
+ "$HOME/.claude/lib/credential-store.sh" get \1 >/dev/null 2>&1 && echo "TOKEN_EXISTS" || echo "TOKEN_MISSING"
334
334
  ```
335
335
 
336
336
  #### 9a. If TOKEN_EXISTS — verify
337
337
 
338
338
  ```bash
339
339
  curl -s -o /dev/null -w "%{http_code}" "{jira.baseUrl}/rest/api/2/myself" \
340
- -H "Authorization: Bearer $(security find-generic-password -a '$USER' -s 'JIRA_TOKEN' -w 2>/dev/null)"
340
+ -H "Authorization: Bearer $("$HOME/.claude/lib/credential-store.sh" get \1 2>/dev/null)"
341
341
  ```
342
342
 
343
343
  - `200` → PASS
@@ -357,7 +357,7 @@ Tell the user:
357
357
  Ask: "Confirm when token is copied to clipboard."
358
358
 
359
359
  ```bash
360
- security delete-generic-password -a "$USER" -s "JIRA_TOKEN" >/dev/null 2>&1; security add-generic-password -a "$USER" -s "JIRA_TOKEN" -w "$(pbpaste)" >/dev/null 2>&1 && echo "TOKEN_STORED" || echo "TOKEN_STORE_FAILED"
360
+ "$HOME/.claude/lib/credential-store.sh" set \1 \"\2\" >/dev/null 2>&1 && echo "TOKEN_STORED" || echo "TOKEN_STORE_FAILED"
361
361
  ```
362
362
 
363
363
  Clear clipboard:
@@ -372,7 +372,7 @@ Verify (repeat 9a). If VPN not connected, accept as `pass (stored, not verified)
372
372
  ### CHECK 10: Confluence Token
373
373
 
374
374
  ```bash
375
- security find-generic-password -a "$USER" -s "CONFLUENCE_TOKEN" -w >/dev/null 2>&1 && echo "TOKEN_EXISTS" || echo "TOKEN_MISSING"
375
+ "$HOME/.claude/lib/credential-store.sh" get \1 >/dev/null 2>&1 && echo "TOKEN_EXISTS" || echo "TOKEN_MISSING"
376
376
  ```
377
377
 
378
378
  Same flow as CHECK 9 but for Confluence:
@@ -419,7 +419,7 @@ Looks up the Figma remote MCP OAuth token (`figu_` prefix) and prints it for the
419
419
  1. **Keychain `FIGMA_MCP_TOKEN`** — the dedicated keychain entry saved by `save-token` or CHECK 3b.
420
420
 
421
421
  ```bash
422
- security find-generic-password -a "$USER" -s "FIGMA_MCP_TOKEN" -w 2>/dev/null
422
+ "$HOME/.claude/lib/credential-store.sh" get \1 2>/dev/null
423
423
  ```
424
424
 
425
425
  2. **Claude Code credentials** — if not in dedicated keychain, extract from Claude Code's credential store.
@@ -484,14 +484,14 @@ Then clear clipboard: `pbcopy < /dev/null`
484
484
  3. **Save to keychain:**
485
485
 
486
486
  ```bash
487
- security delete-generic-password -a "$USER" -s "FIGMA_MCP_TOKEN" >/dev/null 2>&1
488
- security add-generic-password -a "$USER" -s "FIGMA_MCP_TOKEN" -w "$MCP_TOKEN" >/dev/null 2>&1 && echo "TOKEN_SAVED" || echo "TOKEN_SAVE_FAILED"
487
+ "$HOME/.claude/lib/credential-store.sh" delete \1 >/dev/null 2>&1
488
+ "$HOME/.claude/lib/credential-store.sh" set \1 \"\2\" >/dev/null 2>&1 && echo "TOKEN_SAVED" || echo "TOKEN_SAVE_FAILED"
489
489
  ```
490
490
 
491
491
  4. **Verify** the saved token works:
492
492
 
493
493
  ```bash
494
- TOKEN=$(security find-generic-password -a "$USER" -s "FIGMA_MCP_TOKEN" -w 2>/dev/null) && curl -s -o /dev/null -w "%{http_code}" -X POST "https://mcp.figma.com/mcp" -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" -H "Authorization: Bearer $TOKEN" -d '{"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{},"clientInfo":{"name":"setup-check","version":"1.0.0"},"protocolVersion":"2025-03-26"},"id":1}'
494
+ TOKEN=$("$HOME/.claude/lib/credential-store.sh" get \1 2>/dev/null) && curl -s -o /dev/null -w "%{http_code}" -X POST "https://mcp.figma.com/mcp" -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" -H "Authorization: Bearer $TOKEN" -d '{"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{},"clientInfo":{"name":"setup-check","version":"1.0.0"},"protocolVersion":"2025-03-26"},"id":1}'
495
495
  ```
496
496
 
497
497
  - `200` → print "Token saved and verified."
@@ -557,7 +557,7 @@ These are logged but do NOT affect the VALID/INVALID verdict:
557
557
  | Registry file not found | Show command: `cd ${COMMON_PATH} && swift build --product FigmaRegistryUtility && .build/debug/FigmaRegistryUtility update` |
558
558
  | FigmaRegistryUtility not built | Show: `cd ${COMMON_PATH} && swift build --product FigmaRegistryUtility` |
559
559
  | Invalid URL format | Ask user for correct URL via AskUserQuestion |
560
- | Figma token not found | Show: `security add-generic-password -s "FIGMA_ACCESS_TOKEN" -w "<YOUR_TOKEN>"` |
560
+ | Figma token not found | Show: `"$HOME/.claude/lib/credential-store.sh" set FIGMA_ACCESS_TOKEN "<YOUR_TOKEN>"` |
561
561
 
562
562
  ---
563
563
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: figma
2
+ name: figma-to-component
3
3
  description: Figma-to-SwiftUI component implementation workflow. Takes a Figma URL and produces a production-ready SwiftUI component through 8 phases — data gathering, analysis, code generation, testing, Code Connect, and documentation.
4
4
  user-invocable: true
5
5
  argument-hint: <figma-url> | phase-<N> | status