@mmerterden/multi-agent-pipeline 8.6.0 → 8.6.2
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/README.md +30 -49
- package/package.json +4 -1
- package/pipeline/commands/multi-agent/_account-picker.md +1 -1
- package/pipeline/commands/multi-agent/refs/features/dev-critic.md +44 -0
- package/pipeline/commands/multi-agent/refs/features/external-context-injection.md +63 -0
- package/pipeline/commands/multi-agent/refs/features/plan-todos.md +20 -0
- package/pipeline/commands/multi-agent/refs/features/prior-fix-detection.md +49 -0
- package/pipeline/commands/multi-agent/refs/features/repo-map.md +30 -0
- package/pipeline/commands/multi-agent/refs/features/shadow-git.md +24 -0
- package/pipeline/commands/multi-agent/refs/phases/phase-0-init.md +3 -3
- package/pipeline/commands/multi-agent/refs/phases/phase-1-analysis.md +4 -121
- package/pipeline/commands/multi-agent/refs/phases/phase-3-dev.md +4 -75
- package/pipeline/commands/multi-agent/refs/phases/phase-6-commit.md +1 -1
- package/pipeline/commands/multi-agent/setup.md +14 -6
- package/pipeline/commands/multi-agent/sync.md +26 -25
- package/pipeline/schemas/prefs.schema.json +2 -2
- package/pipeline/scripts/fixtures/install-layout.tsv +11 -11
- package/pipeline/scripts/smoke-issue-comment-template.sh +1 -1
- package/pipeline/scripts/smoke-personal-data.sh +5 -3
- package/pipeline/scripts/smoke-plan-todos.sh +5 -2
- package/pipeline/scripts/smoke-sync-adapters.sh +113 -0
- package/pipeline/scripts/smoke-sync-delegation.sh +1 -1
- package/pipeline/scripts/smoke-url-enrichment.sh +1 -1
- package/pipeline/scripts/sync-adapters.mjs +156 -0
- package/pipeline/skills/figma-common/figma-component-confluence-sync/SKILL.md +1 -1
- package/pipeline/skills/figma-common/figma-issue/SKILL.md +5 -5
- package/pipeline/skills/figma-common/figma-setup/SKILL.md +17 -17
- package/pipeline/skills/figma-common/figma-validate/SKILL.md +1 -1
- package/pipeline/skills/figma-ios/figma-to-component/SKILL.md +1 -1
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-6-code-connect.md +5 -5
- package/pipeline/skills/figma-ios/figma-to-component/reference/code-connect.md +1 -1
- package/pipeline/skills/figma-ios/figma-to-component/reference/rest-api-script.md +1 -1
- package/pipeline/skills/figma-ios/figma-to-component/reference/tools.md +1 -1
- package/pipeline/skills/figma-ios/figma-to-component/scripts/phase1-gather.py +1 -1
- package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +1 -1
- package/pipeline/scripts/.last-figma-sync-plan.json +0 -23
|
@@ -10,47 +10,9 @@
|
|
|
10
10
|
|
|
11
11
|
Phase 3 consumes the Phase 2 output object conforming to `pipeline/schemas/planning-output.schema.json` — the task graph (`tasks[]` with `id`, `subject`, `targetFiles`, `complexity`, `blockedBy`) plus the architecture review notes. Tasks execute in dependency order; the schema's `blockedBy` field drives the ready-task picker. In `--dev` mode (no Phase 2), Sonnet/Opus generates the equivalent task list inline before entering the loop below.
|
|
12
12
|
|
|
13
|
-
**Plan Todo iteration (opt-in)**:
|
|
13
|
+
**Plan Todo iteration (opt-in)**: gated by `prefs.global.planTodos.enabled` (default: `false`). When enabled and Phase 2 Step 4.5 emitted a `plan.todos[]`, Phase 3 iterates via `pipeline/lib/plan-todos.sh next/start/complete/fail` instead of walking `tasks[]` directly. When disabled, the loop walks `tasks[]` from `planning-output` — TDD contract is unchanged. Full helper loop + state semantics: `refs/features/plan-todos.md`.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
while next=$(bash "$HOME/.claude/lib/plan-todos.sh" next "$TASK_ID"); [ -n "$next" ]; do
|
|
17
|
-
id=$(printf '%s' "$next" | cut -f1)
|
|
18
|
-
task=$(printf '%s' "$next" | cut -f2)
|
|
19
|
-
bash "$HOME/.claude/lib/plan-todos.sh" start "$TASK_ID" "$id"
|
|
20
|
-
# ... TDD cycle for "$task" ...
|
|
21
|
-
if build_passed; then
|
|
22
|
-
bash "$HOME/.claude/lib/plan-todos.sh" complete "$TASK_ID" "$id" "${COMMIT_HASH} · tests:${TEST_COUNT}"
|
|
23
|
-
else
|
|
24
|
-
bash "$HOME/.claude/lib/plan-todos.sh" fail "$TASK_ID" "$id" "build failed after 3 retries"
|
|
25
|
-
break
|
|
26
|
-
fi
|
|
27
|
-
done
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
When disabled, the loop still walks `tasks[]` from `planning-output` — `plan-todos.sh` adds visibility (status, notes, deps-respecting `next`) but the underlying TDD contract is unchanged.
|
|
31
|
-
|
|
32
|
-
**Shadow-Git checkpoints (opt-in)**: when `prefs.global.shadowGit.enabled` is true, the orchestrator snapshots the worktree via `pipeline/lib/shadow-git.sh` so sub-phase rollback is possible without polluting the project's real `.git` history. Pattern source: Cline checkpoints (<https://docs.cline.bot/features/checkpoints>).
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
# Phase 0 (one-time per task): initialize shadow repo + baseline snapshot.
|
|
36
|
-
bash "$HOME/.claude/lib/shadow-git.sh" init "$TASK_ID" "$WORKTREE"
|
|
37
|
-
|
|
38
|
-
# Per-step (mode: per-todo-step — default): snapshot AFTER each plan-todos
|
|
39
|
-
# step completes successfully, so restore lands on a known-good state.
|
|
40
|
-
bash "$HOME/.claude/lib/plan-todos.sh" complete "$TASK_ID" "$id" "$notes"
|
|
41
|
-
bash "$HOME/.claude/lib/shadow-git.sh" snapshot "$TASK_ID" "$WORKTREE" "step $id: $task"
|
|
42
|
-
|
|
43
|
-
# Per-mutation (mode: per-tool-call): the orchestrator hooks into every Edit/
|
|
44
|
-
# Write/MultiEdit/Bash-mutation tool call. Higher fidelity, ~50ms × tool-call
|
|
45
|
-
# overhead — recommended only for security-critical paths or experimental
|
|
46
|
-
# refactors where the user values fine-grained rollback over speed.
|
|
47
|
-
|
|
48
|
-
# Rollback on demand (interactive helper):
|
|
49
|
-
bash "$HOME/.claude/lib/shadow-git.sh" list "$TASK_ID"
|
|
50
|
-
bash "$HOME/.claude/lib/shadow-git.sh" restore "$TASK_ID" "$WORKTREE" <sha> --files
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Shadow repo location: `~/.claude/state/shadow-git/<task-id>/.git/`. Storage cap: `prefs.shadowGit.pruneAfterDays` (default 14). `/multi-agent:purge` honors this prune; manual cleanup via `shadow-git.sh prune <task-id>`.
|
|
15
|
+
**Shadow-Git checkpoints (opt-in)**: gated by `prefs.global.shadowGit.enabled` (default: `false`). When enabled, the orchestrator snapshots the worktree via `pipeline/lib/shadow-git.sh` so sub-phase rollback is possible without touching the project's real `.git` history. Cline-style. Lifecycle: `shadow-git.sh init` (Phase 0 baseline), `shadow-git.sh snapshot` (per step after `plan-todos complete`), `shadow-git.sh restore <sha> --files` (rollback). Modes: `per-todo-step` (default) or `per-tool-call`. Full wiring + storage cap: `refs/features/shadow-git.md`.
|
|
54
16
|
|
|
55
17
|
#### Component tasks — delegated dispatch (taskType === "component")
|
|
56
18
|
|
|
@@ -239,40 +201,7 @@ release_build_lock() { rm -rf "$BUILD_LOCK"; }
|
|
|
239
201
|
|
|
240
202
|
#### Step 3.5 — Dev Critic (Evaluator-Optimizer, opt-in)
|
|
241
203
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
**Gated by `prefs.global.devCritic.enabled`** (default: `false`). When enabled, after the generator's last edit and BEFORE Phase 4:
|
|
245
|
-
|
|
246
|
-
1. Dispatch `dev-critic` sub-agent (Sonnet by default, tools: `Read, Grep, Glob, Bash`).
|
|
247
|
-
2. Critic runs 4 mandatory deterministic gates (build / lint / test / secrets) — failure of any → `pass: false` with `blocking` finding.
|
|
248
|
-
3. If gates green, critic walks the platform checklist (iOS 13-item / Android Kotlin / Backend generic — selected by Phase 1 `detectedStack`).
|
|
249
|
-
4. Returns schema-validated JSON (`pipeline/schemas/dev-critic-output.schema.json`): `{pass, iteration, gates, findings[], escalate}`.
|
|
250
|
-
|
|
251
|
-
**Loop cap (STRICT)**: max **2** iterations.
|
|
252
|
-
|
|
253
|
-
| Round | Behavior |
|
|
254
|
-
|---|---|
|
|
255
|
-
| 1 | Full critic pass — all gates + full checklist |
|
|
256
|
-
| 2 | Re-check only round-1 failures. No new findings allowed (scope creep prevention) |
|
|
257
|
-
| 3+ | NOT ALLOWED. Critic returns `escalate: true`; orchestrator pauses (interactive) or proceeds to Phase 4 with logged failure (autopilot) |
|
|
258
|
-
|
|
259
|
-
**Action by severity:**
|
|
260
|
-
|
|
261
|
-
- `blocking` → generator MUST fix; another Dev iteration before re-critic
|
|
262
|
-
- `important` → generator SHOULD fix; if skipped, pass through to Phase 4 (legitimate reviewer ground)
|
|
263
|
-
- `suggestion` → generator's judgement; never blocks round 2
|
|
264
|
-
|
|
265
|
-
**Telemetry**: each critic call emits `dev_critic.call` with `iteration`, `pass`, `gates_failed`, `blocking`, `important`, `duration_ms`, `tokens_in/out`. Phase 7 cost rollup lists these as `phase 3.5` line items so the net saving (Phase 4 reviewer/triage calls avoided) is measurable.
|
|
266
|
-
|
|
267
|
-
**Off by default reason**: introduces ~1× Sonnet call per Dev iteration. On simple bug fixes the cost outweighs the benefit (Phase 4 would have caught the same thing for similar cost). Recommended on:
|
|
268
|
-
|
|
269
|
-
- Feature work (≥200 LOC diff)
|
|
270
|
-
- Security-touching paths (auth, keychain, network)
|
|
271
|
-
- Multi-file refactors where rule violations compound
|
|
272
|
-
|
|
273
|
-
**Reference**: see `pipeline/agents/dev-critic.md` for the full agent specification (mandatory gates, checklist enumeration, output schema, severity semantics).
|
|
274
|
-
|
|
275
|
-
**Why this fits orchestrator-workers + evaluator-optimizer hybrid**: Phase 4 is parallelization-with-voting — good for *adversarial* perspectives (security, architecture). Phase 3.5 is evaluator-optimizer — good for *deterministic* criteria (build, tests, checklists). Sending failing builds into Phase 4 wastes 2–3 reviewer calls + Opus triage; Phase 3.5 absorbs that cost at one Sonnet call.
|
|
204
|
+
Gated by `prefs.global.devCritic.enabled` (default: `false`). When enabled, after the generator's last edit and BEFORE Phase 4: dispatch `dev-critic` sub-agent (Sonnet), run 4 deterministic gates (build / lint / test / secrets), then platform checklist. STRICT loop cap — **max 2 iterations** (round 2 re-checks round-1 failures only); round 3+ returns `escalate: true`. Severity routing: `blocking` → generator must fix; `important` → SHOULD fix or pass through to Phase 4; `suggestion` → generator's judgement. Full agent contract, schema, telemetry, when-to-enable: `refs/features/dev-critic.md` + `pipeline/agents/dev-critic.md`.
|
|
276
205
|
|
|
277
206
|
---
|
|
278
207
|
|
|
@@ -302,7 +231,7 @@ When `agent-state.json.onlyDevelop === true`, Phase 3 runs self-contained with *
|
|
|
302
231
|
|
|
303
232
|
---
|
|
304
233
|
|
|
305
|
-
#### Multi-Repo Mode
|
|
234
|
+
#### v2.1.0+ Multi-Repo Mode
|
|
306
235
|
|
|
307
236
|
Active when `state.projects[].length > 1` (set by Phase 0 multi-select). Single-repo flow above is preserved verbatim — this section adds the deltas.
|
|
308
237
|
|
|
@@ -216,7 +216,7 @@ Before creating PR, ask: "DRAFT or READY? [1/2], default 1" — DRAFT blocks mer
|
|
|
216
216
|
|
|
217
217
|
---
|
|
218
218
|
|
|
219
|
-
#### Multi-Repo Mode
|
|
219
|
+
#### v2.1.0+ Multi-Repo Mode
|
|
220
220
|
|
|
221
221
|
Active when `state.projects[].length > 1`. The single-repo flow above is preserved verbatim — multi-repo is an extension, not a replacement.
|
|
222
222
|
|
|
@@ -259,19 +259,23 @@ pbcopy < /dev/null
|
|
|
259
259
|
|
|
260
260
|
(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.)
|
|
261
261
|
|
|
262
|
-
For JSON file (Firebase):
|
|
262
|
+
For JSON file (Firebase) — same clipboard flow, JSON content never lands in argv or shell history:
|
|
263
263
|
```
|
|
264
|
-
|
|
265
|
-
|
|
264
|
+
Open the Service Account JSON, copy its FULL contents to your clipboard,
|
|
265
|
+
then press Enter.
|
|
266
|
+
(Contents will be base64-encoded and saved to Keychain automatically)
|
|
266
267
|
|
|
267
|
-
|
|
268
|
+
Press Enter when ready...
|
|
268
269
|
```
|
|
269
270
|
|
|
270
271
|
Pipeline runs silently:
|
|
271
272
|
```bash
|
|
272
|
-
~/.claude/lib/credential-store.sh set "<KEY_NAME>" "$(
|
|
273
|
+
pbpaste | base64 | ~/.claude/lib/credential-store.sh set "<KEY_NAME>" "$(cat)"
|
|
274
|
+
pbcopy < /dev/null # clear clipboard
|
|
273
275
|
```
|
|
274
276
|
|
|
277
|
+
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`).
|
|
278
|
+
|
|
275
279
|
For GitHub (special case):
|
|
276
280
|
```
|
|
277
281
|
! gh auth login
|
|
@@ -699,7 +703,11 @@ pbcopy < /dev/null # clear clipboard
|
|
|
699
703
|
# Plain text (direct value)
|
|
700
704
|
~/.claude/lib/credential-store.sh set "<SERVICE_NAME>" "<VALUE>"
|
|
701
705
|
|
|
702
|
-
# JSON file (
|
|
706
|
+
# JSON file from clipboard (preferred — no file path leaks into history)
|
|
707
|
+
pbpaste | base64 | ~/.claude/lib/credential-store.sh set "<SERVICE_NAME>" "$(cat)"
|
|
708
|
+
pbcopy < /dev/null
|
|
709
|
+
|
|
710
|
+
# JSON file from disk (fallback when clipboard backend unavailable)
|
|
703
711
|
~/.claude/lib/credential-store.sh set "<SERVICE_NAME>" "$(base64 < /path/to/file.json)"
|
|
704
712
|
|
|
705
713
|
# Read
|
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -311,7 +312,7 @@ refactor, resume, review, scan, search, setup, stack, status, sync, test, update
|
|
|
311
312
|
| Islem | Token Kaynagi |
|
|
312
313
|
|-------|---------------|
|
|
313
314
|
| `gh` CLI (personal) | `{owner}` gh auth (Keychain) |
|
|
314
|
-
| `gh` CLI (work) | `
|
|
315
|
+
| `gh` CLI (work) | `{user}_<work-label>` gh auth (Keychain) — see `prefs.global.accounts[]` |
|
|
315
316
|
| npm publish | `NODE_AUTH_TOKEN` — CI'da `GITHUB_TOKEN`, local'de Keychain PAT |
|
|
316
317
|
|
|
317
318
|
```bash
|
|
@@ -977,7 +977,7 @@
|
|
|
977
977
|
"properties": {
|
|
978
978
|
"id": {
|
|
979
979
|
"type": "string",
|
|
980
|
-
"description": "Short id from account-resolver, e.g.
|
|
980
|
+
"description": "Short id from account-resolver, e.g. my-account"
|
|
981
981
|
},
|
|
982
982
|
"prefix": {
|
|
983
983
|
"type": "string",
|
|
@@ -997,7 +997,7 @@
|
|
|
997
997
|
},
|
|
998
998
|
"accounts": {
|
|
999
999
|
"type": "array",
|
|
1000
|
-
"description": "v7.4.0+. Per-account overrides \u2014 host, label, default platform. Resolved by account-resolver.sh by matching `id` (e.g.
|
|
1000
|
+
"description": "v7.4.0+. Per-account overrides \u2014 host, label, default platform. Resolved by account-resolver.sh by matching `id` (e.g. my-account). When unset, hosts fall back to global.hosts.{jira,bitbucket}.",
|
|
1001
1001
|
"items": {
|
|
1002
1002
|
"type": "object",
|
|
1003
1003
|
"additionalProperties": false,
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
.claude/CLAUDE.md 1
|
|
2
|
-
.claude/agents
|
|
3
|
-
.claude/commands
|
|
4
|
-
.claude/lib
|
|
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
|
|
8
|
-
.claude/scripts
|
|
7
|
+
.claude/schemas 20
|
|
8
|
+
.claude/scripts 143
|
|
9
9
|
.claude/settings.json 1
|
|
10
|
-
.claude/skills
|
|
11
|
-
.copilot/agents
|
|
10
|
+
.claude/skills 414
|
|
11
|
+
.copilot/agents 8
|
|
12
12
|
.copilot/copilot-instructions.md 1
|
|
13
|
-
.copilot/lib
|
|
14
|
-
.copilot/schemas
|
|
15
|
-
.copilot/scripts
|
|
16
|
-
.copilot/skills
|
|
13
|
+
.copilot/lib 18
|
|
14
|
+
.copilot/schemas 20
|
|
15
|
+
.copilot/scripts 143
|
|
16
|
+
.copilot/skills 444
|
|
@@ -40,8 +40,11 @@ FORBIDDEN=(
|
|
|
40
40
|
'mmerterden\.dev'
|
|
41
41
|
'mmerterden/remote-control'
|
|
42
42
|
'mmerterden/mmerterden\.dev'
|
|
43
|
-
# Author's macOS username
|
|
44
|
-
|
|
43
|
+
# Author's macOS username — both forms: M_ERDEN3 (underscore) appears in
|
|
44
|
+
# Keychain labels, M-ERDEN3 (dash) appears in gh auth usernames + paths
|
|
45
|
+
# encoded by the Claude projects convention (/ → -).
|
|
46
|
+
'\bM_ERDEN3\b'
|
|
47
|
+
'\bM-ERDEN3\b'
|
|
45
48
|
)
|
|
46
49
|
|
|
47
50
|
PASS=0
|
|
@@ -56,7 +59,6 @@ for pattern in "${FORBIDDEN[@]}"; do
|
|
|
56
59
|
--exclude="smoke-personal-data.sh" \
|
|
57
60
|
--exclude="smoke-install-leak-gate.sh" \
|
|
58
61
|
--exclude="figma-placeholder-map.json" \
|
|
59
|
-
--exclude=".last-figma-sync-plan.json" \
|
|
60
62
|
--exclude="REVIEW_CHECKLIST.md" \
|
|
61
63
|
"$pattern" "$PIPELINE_DIR" 2>/dev/null || true)
|
|
62
64
|
if [ -n "$matches" ]; then
|
|
@@ -95,8 +95,11 @@ else
|
|
|
95
95
|
fi
|
|
96
96
|
|
|
97
97
|
# 5-8. Lifecycle test against a temp state dir
|
|
98
|
+
# Use a temp directory derived from $HOME so the path stays cross-user.
|
|
98
99
|
TEST_TASK="SMOKE-PT-$$"
|
|
99
|
-
|
|
100
|
+
TEST_PROJECT_KEY=$(printf '%s' "$HOME/test-plan-todos-smoke" | tr '/' '-')
|
|
101
|
+
TEST_PROJECT_ROOT="$HOME/.claude/projects/$TEST_PROJECT_KEY"
|
|
102
|
+
TEST_DIR="$TEST_PROJECT_ROOT/state/$TEST_TASK"
|
|
100
103
|
mkdir -p "$TEST_DIR"
|
|
101
104
|
echo '{"task":{"id":"'"$TEST_TASK"'"}}' > "$TEST_DIR/agent-state.json"
|
|
102
105
|
PLAN='{"title":"Smoke","todos":[
|
|
@@ -157,7 +160,7 @@ else
|
|
|
157
160
|
fi
|
|
158
161
|
|
|
159
162
|
# Cleanup the test state
|
|
160
|
-
rm -rf "$
|
|
163
|
+
rm -rf "$TEST_PROJECT_ROOT"
|
|
161
164
|
|
|
162
165
|
# 9. Prefs schema toggle
|
|
163
166
|
if jq -e '.properties.global.properties.planTodos.properties.enabled
|
|
@@ -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 -
|
|
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
|
|
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=$(
|
|
38
|
+
CONFLUENCE_TOKEN=$("$HOME/.claude/lib/credential-store.sh" get \1 2>/dev/null)
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
---
|