@mmerterden/multi-agent-pipeline 14.2.2 → 15.1.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.
Files changed (132) hide show
  1. package/CHANGELOG.md +186 -6
  2. package/README.md +19 -12
  3. package/README.tr.md +19 -12
  4. package/SECURITY.md +43 -0
  5. package/docs/FIGMA_PIPELINE.md +3 -3
  6. package/docs/adr/0006-skills-core-external-split.md +1 -1
  7. package/docs/adr/0007-multi-tool-adapter-framework.md +1 -1
  8. package/docs/adr/0009-claude-stack-skills-plugin-only.md +31 -0
  9. package/docs/adr/README.md +1 -0
  10. package/docs/architecture.md +13 -13
  11. package/docs/ecosystem.md +31 -31
  12. package/docs/features.md +5 -5
  13. package/index.js +6 -1
  14. package/install/_codex-agents.mjs +11 -2
  15. package/install/_common.mjs +109 -3
  16. package/install/_dev-only-files.mjs +0 -1
  17. package/install/_platform-filter.mjs +54 -113
  18. package/install/_plugin-skills.mjs +36 -36
  19. package/install/claude.mjs +251 -61
  20. package/install/codex.mjs +28 -6
  21. package/install/copilot.mjs +69 -9
  22. package/install/index.mjs +9 -3
  23. package/install/templates/codex-instructions.md +1 -1
  24. package/install/templates/copilot-instructions.md +3 -3
  25. package/package.json +2 -3
  26. package/pipeline/commands/multi-agent/SKILL.md +2 -0
  27. package/pipeline/commands/multi-agent/analysis/SKILL.md +3 -3
  28. package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +2 -2
  29. package/pipeline/commands/multi-agent/build-optimize/SKILL.md +9 -9
  30. package/pipeline/commands/multi-agent/channels/SKILL.md +1 -1
  31. package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +186 -0
  32. package/pipeline/commands/multi-agent/dev/SKILL.md +1 -1
  33. package/pipeline/commands/multi-agent/dev-autopilot/SKILL.md +1 -1
  34. package/pipeline/commands/multi-agent/dev-local/SKILL.md +1 -1
  35. package/pipeline/commands/multi-agent/dev-local-autopilot/SKILL.md +1 -1
  36. package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
  37. package/pipeline/commands/multi-agent/help/SKILL.md +19 -4
  38. package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +2 -2
  39. package/pipeline/commands/multi-agent/jira/SKILL.md +1 -1
  40. package/pipeline/commands/multi-agent/prune-prompts/SKILL.md +81 -0
  41. package/pipeline/commands/multi-agent/refactor/SKILL.md +36 -1
  42. package/pipeline/commands/multi-agent/resume/SKILL.md +1 -1
  43. package/pipeline/commands/multi-agent/{ship → resume-local}/SKILL.md +8 -8
  44. package/pipeline/commands/multi-agent/scan/SKILL.md +1 -1
  45. package/pipeline/commands/multi-agent/setup/SKILL.md +5 -5
  46. package/pipeline/commands/multi-agent/stack/SKILL.md +62 -40
  47. package/pipeline/commands/multi-agent/store-ready/SKILL.md +3 -3
  48. package/pipeline/commands/multi-agent/sync/SKILL.md +18 -11
  49. package/pipeline/commands/multi-agent/testflight-validation/SKILL.md +1 -1
  50. package/pipeline/commands/multi-agent/uninstall/SKILL.md +2 -0
  51. package/pipeline/commands/multi-agent/update/SKILL.md +4 -4
  52. package/pipeline/lib/issue-fetcher.sh +1 -1
  53. package/pipeline/lib/parse-complaints.sh +316 -0
  54. package/pipeline/multi-agent-refs/channels/wiki.md +3 -3
  55. package/pipeline/multi-agent-refs/complaint-analysis-template.md +99 -0
  56. package/pipeline/multi-agent-refs/component-dispatch.md +6 -6
  57. package/pipeline/multi-agent-refs/cross-cli-contract.md +16 -16
  58. package/pipeline/multi-agent-refs/features/external-context-injection.md +1 -1
  59. package/pipeline/multi-agent-refs/features/stack-skill-routing.md +5 -5
  60. package/pipeline/multi-agent-refs/generate-issue.md +1 -1
  61. package/pipeline/multi-agent-refs/phases/modes.md +1 -1
  62. package/pipeline/multi-agent-refs/phases/operations.md +7 -1
  63. package/pipeline/multi-agent-refs/phases/phase-0-init.md +1 -1
  64. package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +7 -7
  65. package/pipeline/multi-agent-refs/phases/phase-2-planning.md +5 -5
  66. package/pipeline/multi-agent-refs/phases/phase-3-dev.md +3 -3
  67. package/pipeline/multi-agent-refs/phases/phase-4-review.md +12 -12
  68. package/pipeline/multi-agent-refs/phases/phase-5-test.md +1 -1
  69. package/pipeline/multi-agent-refs/phases/phase-7-report.md +6 -0
  70. package/pipeline/multi-agent-refs/tracker-contract.md +3 -2
  71. package/pipeline/multi-agent-refs/wiki-capture.md +2 -2
  72. package/pipeline/preferences-template.json +18 -5
  73. package/pipeline/rules/figma-pipeline.md +2 -2
  74. package/pipeline/schemas/agent-state.schema.json +1 -1
  75. package/pipeline/schemas/complaint-analysis-spec.schema.json +216 -0
  76. package/pipeline/schemas/migrations/prefs-2.5.0-to-2.6.0.mjs +46 -0
  77. package/pipeline/schemas/prefs.schema.json +296 -66
  78. package/pipeline/schemas/token-budget.json +2 -2
  79. package/pipeline/scripts/README.md +4 -3
  80. package/pipeline/scripts/_stack-routing.mjs +79 -0
  81. package/pipeline/scripts/audit-log-rotate.sh +4 -1
  82. package/pipeline/scripts/build-skills-index.mjs +11 -0
  83. package/pipeline/scripts/build-stack-plugins.mjs +28 -60
  84. package/pipeline/scripts/check-derived-drift.mjs +55 -28
  85. package/pipeline/scripts/gc-worktrees.sh +4 -1
  86. package/pipeline/scripts/gen-skills-index.mjs +1 -1
  87. package/pipeline/scripts/match-skills.mjs +12 -2
  88. package/pipeline/scripts/migrate-prefs.mjs +33 -21
  89. package/pipeline/scripts/phase-tracker.sh +32 -5
  90. package/pipeline/scripts/phase0-exit-gate.mjs +3 -2
  91. package/pipeline/scripts/run-aggregator.mjs +7 -2
  92. package/pipeline/scripts/scan-agent-config.sh +1 -1
  93. package/pipeline/scripts/skill-conformance.mjs +165 -30
  94. package/pipeline/scripts/smoke-cross-cli-behavior.sh +1 -1
  95. package/pipeline/scripts/test-gap-rules/android.json +25 -0
  96. package/pipeline/scripts/test-gap-rules/ios.json +34 -0
  97. package/pipeline/scripts/test-gap-rules/node.json +29 -0
  98. package/pipeline/scripts/test-gap-rules/python.json +25 -0
  99. package/pipeline/scripts/uninstall.mjs +160 -11
  100. package/pipeline/scripts/usage-report.mjs +426 -0
  101. package/pipeline/scripts/validate-complaint-doc.mjs +250 -0
  102. package/pipeline/scripts/validate-reviewer.mjs +9 -3
  103. package/pipeline/skills/.skill-manifest.json +156 -108
  104. package/pipeline/skills/.skills-index.json +449 -12
  105. package/pipeline/skills/shared/README.md +14 -10
  106. package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +1 -1
  107. package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
  108. package/pipeline/skills/shared/core/multi-agent-complaint-analysis/SKILL.md +49 -0
  109. package/pipeline/skills/shared/core/multi-agent-dev/SKILL.md +1 -1
  110. package/pipeline/skills/shared/core/multi-agent-dev-autopilot/SKILL.md +1 -1
  111. package/pipeline/skills/shared/core/multi-agent-dev-local/SKILL.md +1 -1
  112. package/pipeline/skills/shared/core/multi-agent-dev-local-autopilot/SKILL.md +1 -1
  113. package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +2 -2
  114. package/pipeline/skills/shared/core/multi-agent-prune-prompts/SKILL.md +83 -0
  115. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +153 -90
  116. package/pipeline/skills/shared/core/{multi-agent-ship → multi-agent-resume-local}/SKILL.md +6 -6
  117. package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +89 -22
  118. package/pipeline/skills/shared/core/multi-agent-store-ready/SKILL.md +1 -1
  119. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +8 -8
  120. package/pipeline/skills/shared/core/multi-agent-testflight-validation/SKILL.md +1 -1
  121. package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +1 -1
  122. package/pipeline/skills/shared/external/ios-coding-standard/modules/_TEMPLATE.yml +2 -2
  123. package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +368 -33
  124. package/pipeline/skills/shared/external/ios-coding-standard/references/swiftlint.draft.yml +1 -2
  125. package/pipeline/skills/shared/external/ios-coding-standard/scripts/check_structure.py +765 -0
  126. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +75 -0
  127. package/pipeline/skills/shared/external/ios-module-structure/modules/_TEMPLATE.yml +131 -0
  128. package/pipeline/skills/shared/external/ios-module-structure/references/rules.yml +559 -0
  129. package/pipeline/skills/shared/external/ios-module-structure/scripts/check_structure.py +765 -0
  130. package/pipeline/skills/shared/external/localization-reuse-map/example-mapping.json +53 -10
  131. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +4 -3
  132. package/pipeline/skills/skills-index.md +7 -4
@@ -59,8 +59,8 @@ Run every step automatically:
59
59
  ```
60
60
  Step 1: PLATFORM Detect macOS / Linux / Windows (Git Bash / WSL); export PLATFORM env
61
61
  Step 1.5: DETECT Compare timestamps, find stale targets
62
- Step 2: COPILOT Claude Code -> Copilot CLI (instructions + 49 sub-command skills)
63
- Step 2b: CODEX Claude Code -> Codex CLI (1 router skill + 49 specs as refs + 8 agent TOML)
62
+ Step 2: COPILOT Claude Code -> Copilot CLI (instructions + 51 sub-command skills)
63
+ Step 2b: CODEX Claude Code -> Codex CLI (1 router skill + 51 specs as refs + 8 agent TOML)
64
64
  Step 3: REPO Claude Code -> pipeline repo (genericized, personal data scrub, bash -n on all sh)
65
65
  Step 3c: PLUGINS pipeline shared/external -> multi-agent-plugins marketplace (rebuild knowledge/,
66
66
  bump changed plugins' patch version, commit + push the plugins repo)
@@ -127,13 +127,13 @@ If nothing is stale → report "All targets up to date" and stop.
127
127
  - `~/.claude/CLAUDE.md`, `~/.claude/rules/`, `~/.claude/knowledge/`
128
128
  - `~/.claude/scripts/` - EXCEPT `pre-commit-check.sh`, `agent-guard.sh`, `agent-guard.py`, and `build-stack-plugins.mjs` (generic, synced)
129
129
  - `~/.claude/settings.json`
130
- - **Any `~/.claude/commands/multi-agent/*.md` whose frontmatter has `local-only: true`** - these are user/repo-specific alias wrappers that delegate to a private marketplace plugin (e.g. corporate `ai-ios-engineering-toolkit` skills exposed as `multi-agent:<name>`). Syncing them would leak the private plugin/skill names into the public pipeline. Filter before copy: skip every source file containing `local-only: true`, and after copy assert none reached `pipeline/commands/`.
130
+ - **Any `~/.claude/commands/multi-agent/*.md` whose frontmatter has `local-only: true`** - these are user/repo-specific alias wrappers that delegate to a private marketplace plugin (e.g. corporate `ai-ios-toolkit` skills exposed as `multi-agent:<name>`). Syncing them would leak the private plugin/skill names into the public pipeline. Filter before copy: skip every source file containing `local-only: true`, and after copy assert none reached `pipeline/commands/`.
131
131
  ```bash
132
132
  # backstop: no local-only wrapper may exist in the synced target
133
133
  grep -rl "^local-only: true" ~/multi-agent-pipeline/pipeline/commands/ 2>/dev/null \
134
134
  && { echo "ABORT: local-only wrapper leaked into pipeline/commands"; exit 1; } || true
135
135
  # backstop: no corporate marketplace/skill reference in the synced tree
136
- grep -rniE "ai-ios-engineering-toolkit:(create-ui-component|evolve-ui-component|fix-bug|branch-and-pr|backlog|code-connect|figma-utility|resource-utility|component-wiki|component-docs|figma-setup)" \
136
+ grep -rniE "ai-ios(-engineering)?-toolkit:(create-ui-component|evolve-ui-component|fix-bug|branch-and-pr|backlog|code-connect|figma-utility|resource-utility|component-wiki|component-docs|figma-setup)" \
137
137
  ~/multi-agent-pipeline/pipeline/commands/ 2>/dev/null \
138
138
  && { echo "ABORT: corporate reference leaked into pipeline/commands"; exit 1; } || true
139
139
  ```
@@ -176,7 +176,7 @@ If nothing is stale → report "All targets up to date" and stop.
176
176
  Unlike the Copilot step, this one does **not** hand-copy files. The Codex tree is a
177
177
  *transform* of the Claude tree, not a mirror of it, and the transform is real work:
178
178
 
179
- - the 49 sub-command specs become reference files, because Codex silently truncates
179
+ - the 51 sub-command specs become reference files, because Codex silently truncates
180
180
  its skills block (see `cross-cli-contract.md` 2.6 for the measurement)
181
181
  - every `$HOME/.claude/...` reference to a CLI-owned tree is retargeted, with
182
182
  `agents/<persona>.md` becoming `.toml` and the dispatcher becoming the router skill
@@ -233,13 +233,20 @@ node "$HOME/multi-agent-pipeline/pipeline/scripts/build-stack-plugins.mjs" --dry
233
233
 
234
234
  # 2. Apply: rebuild each stack plugin's knowledge/ from shared/external,
235
235
  # regenerate plugin.json skills[], bump the patch version of any plugin
236
- # whose skill set changed. Cross-stack skills -> ai-common-engineering-toolkit;
236
+ # whose skill set changed. Cross-stack skills -> ai-common-toolkit;
237
237
  # Apple/Xcode-only skills -> iOS plugin only.
238
238
  node "$HOME/multi-agent-pipeline/pipeline/scripts/build-stack-plugins.mjs"
239
239
  ```
240
240
 
241
241
  **Version-based management:** the generator bumps a plugin's `version` (patch) only when its skill set actually changed. Idempotent - a no-op run bumps nothing. Consumers pick up new versions via `/multi-agent:update` (which runs `claude marketplace update multi-agent-plugins`).
242
242
 
243
+ **Help + README freshness (mandatory when the generator reported any change):**
244
+
245
+ 1. Every plugin ships a `tools/help` skill that renders its catalog LIVE from `plugin.json`, so it needs no per-skill edits - but verify each changed plugin still registers it (`grep -c "./skills/tools/help" plugins/<name>/.claude-plugin/plugin.json` = 1) and that a new plugin gets one before publish.
246
+ 2. Plugin README tables sync through `python3 tools/bump.py <plugin> --set <version>`; run `python3 tools/bump.py --check` and treat DRIFT as a blocker.
247
+ 3. `python3 tools/validate.py` must be clean (it also enforces the CHANGELOG heading for every bumped version - write the entry, do not backdate).
248
+ 4. When the pipeline's command surface changed in the same sync (new/renamed `/multi-agent:*` commands), confirm `commands/multi-agent/help/SKILL.md` and both pipeline READMEs (`README.md`, `README.tr.md`) were updated in Step 3 before pushing here.
249
+
243
250
  **Publish (only if the generator reported bumps):**
244
251
 
245
252
  ```bash
@@ -484,21 +491,21 @@ When invoked with the `release` argument:
484
491
  > Codex takes the Step 2b path instead; see that section.
485
492
 
486
493
  This runs on the Claude <-> Copilot axis. Codex is NOT synced here: it receives the
487
- same 49 specs as reference files rather than as peer skills, via Step 2b - see
494
+ same 51 specs as reference files rather than as peer skills, via Step 2b - see
488
495
  `cross-cli-contract.md` 2.6 for why the parity axis differs per host.
489
496
 
490
497
  | Claude Code | Copilot CLI |
491
498
  |-------------|-------------|
492
499
  | `~/.claude/commands/multi-agent/{cmd}/SKILL.md` | `~/.copilot/skills/multi-agent-{cmd}/SKILL.md` |
493
500
 
494
- **49 commands are synced** (canonical inventory - must match `cross-cli-contract.md` section 1; drift = contract violation):
501
+ **51 commands are synced** (canonical inventory - must match `cross-cli-contract.md` section 1; drift = contract violation):
495
502
 
496
503
  ```
497
- analysis, analysis-resolve, autopilot, build-optimize, channels, create-jira, design-check, dev,
504
+ analysis, analysis-resolve, autopilot, build-optimize, channels, complaint-analysis, create-jira, design-check, dev,
498
505
  dev-autopilot, dev-local, dev-local-autopilot, diff-explain, forget, garbage-collect,
499
506
  help, ios-coding-standard, issue, jira, kill, language, local,
500
- local-autopilot, log, manual-test, prune-logs, purge, refactor, resume, review, review-issue, review-jira,
501
- routines, save, scan, search, setup, ship, stack, status, store-ready, sync, test, test-accessibility,
507
+ local-autopilot, log, manual-test, prune-logs, prune-prompts, purge, refactor, resume, review, review-issue, review-jira,
508
+ routines, save, scan, search, setup, resume-local, stack, status, store-ready, sync, test, test-accessibility,
502
509
  test-dark-mode, test-dynamic-type, test-screenshots, testflight-validation, uninstall, update
503
510
  ```
504
511
 
@@ -40,7 +40,7 @@ Unchanged from before the merge, on the iOS path:
40
40
  |---|---|---|
41
41
  | **1. Static** | `ios_app_store_audit` (18 rules, real ITMS codes) | an `.xcarchive` |
42
42
  | **2. Authoritative** | `ios_testflight_validate` → `altool --validate-app` | an `.ipa` + credentials |
43
- | **3. Policy** | `app-store-review` skill vs repo source | repo checkout |
43
+ | **3. Policy** | `ai-ios-toolkit:app-store-review` skill vs repo source | repo checkout |
44
44
 
45
45
  A skipped gate is never folded into the pass count, and the run never uploads. The
46
46
  report lands in `~/StoreChecks/ios-<repo>-<branch>-<timestamp>/report.md`.
@@ -28,6 +28,8 @@ Tokens in the OS credential store are NEVER removed by either mode.
28
28
  - `~/.claude/CLAUDE.md` (user customisations)
29
29
  - `~/.claude/rules/` (user-owned; installed write-if-missing, never overwritten)
30
30
  - User content OUTSIDE the `<!-- multi-agent-pipeline:begin/end -->` markers (`copilot-instructions.md`)
31
+ - Local-only alias wrappers (`local-only: true` frontmatter) under `commands/multi-agent/` (user-authored, exist nowhere else)
32
+ - User-authored skill dirs and agent files (only pipeline-shipped names are removed)
31
33
 
32
34
  Settings (`multi-agent-preferences.json`) and the log root (`~/.claude/logs/multi-agent/`) are kept by default and removed only when the user picks full cleanup (`--all-data`).
33
35
 
@@ -99,12 +99,12 @@ Update the pipeline in one command. Existing preferences are preserved; only ski
99
99
  ## Output
100
100
 
101
101
  ```
102
- Current: v3.6.0
102
+ Current: v14.2.2
103
103
  -> git pull origin main
104
- -> node install.js --all (49 commands, 50 scripts, 202 skills)
105
- -> migrate-prefs.mjs (0 changes - already v2.1.0)
104
+ -> node install.js --all (51 commands, 228 scripts, 205 skills)
105
+ -> migrate-prefs.mjs (0 changes - already v2.6.0)
106
106
 
107
- ✓ Updated: v3.6.0 → v3.7.0
107
+ ✓ Updated: v14.2.2 → v15.0.0
108
108
 
109
109
  Changes:
110
110
  e3f883e fix(dev): Phase 6 must show local test prompt
@@ -402,7 +402,7 @@ sys.stdout.write((f.get("description") or "").replace("\x1f", " "))
402
402
  gh-url)
403
403
  OWNER=$(printf '%s' "$INPUT" | sed -E 's#https?://github.com/##; s#/.*##')
404
404
  REPO=$(printf '%s' "$INPUT" | sed -E 's#https?://github.com/[^/]+/##; s#/.*##')
405
- NUM=$(printf '%s' "$INPUT" | sed -E 's#.*/issues/##; s#[/?].*##')
405
+ NUM=$(printf '%s' "$INPUT" | sed -E 's#.*/issues/##; s#[/?#].*##')
406
406
  raw=$(fetch_github "$OWNER" "$REPO" "$NUM") || raw=""
407
407
  if [ -n "$raw" ]; then
408
408
  # Single python3 pass - see jira-id branch for rationale on the
@@ -0,0 +1,316 @@
1
+ #!/bin/bash
2
+ #
3
+ # parse-complaints.sh
4
+ # Normalize a batch of customer complaints into the uniform JSON list the
5
+ # /multi-agent:complaint-analysis command consumes. Accepts a file export
6
+ # (csv / xlsx / txt / json) or pasted free text on stdin, extracts any
7
+ # transaction / conversation ids embedded in the text, and redacts common
8
+ # PII shapes before anything leaves this script. The raw input is never
9
+ # copied into pipeline state - only the redacted view is.
10
+ #
11
+ # Input forms (exactly one source is required):
12
+ # ./parse-complaints.sh --file <path> [--format csv|xlsx|txt|json|auto]
13
+ # ./parse-complaints.sh --stdin [--format text]
14
+ #
15
+ # Options:
16
+ # --no-redact keep complaint text verbatim (debugging only; the skill
17
+ # never passes this flag)
18
+ #
19
+ # Column mapping (file formats, case-insensitive header match):
20
+ # trxId <- trxId | trx | transaction id / transaction_id / transactionId
21
+ # convId <- convId | conversationId | x-conversationId
22
+ # date <- date | created | createdAt | timestamp
23
+ # platformHint <- platform | channel | os | device
24
+ # text <- comment | complaint | text | description | message, else the
25
+ # longest text column
26
+ #
27
+ # Free-text mode splits on blank lines (or numbered bullets when the paste is
28
+ # one block) and pre-fills ids per block using the same label set as
29
+ # context-link-extractor.sh GRAYLOG_RE (x-conversationId | conversationId |
30
+ # convId | trxId | trx | transaction id).
31
+ #
32
+ # Redaction (default ON, applied after id extraction so ids survive):
33
+ # email -> [REDACTED:email]
34
+ # 13-19 digit card-like runs -> [REDACTED:card]
35
+ # 11 digit national-id-like numbers -> [REDACTED:national-id]
36
+ # 9+ digit phone-like runs -> [REDACTED:phone]
37
+ # 6-char PNR-like tokens (A-Z0-9 mix)-> [REDACTED:pnr]
38
+ #
39
+ # Output (stdout, single JSON object):
40
+ # {
41
+ # "parsedAt": "<ISO8601>",
42
+ # "source": "<path>"|"stdin",
43
+ # "format": "csv|xlsx|txt|json|text",
44
+ # "complaints": [
45
+ # { "id": "C-01", "text": "...", "trxId": "..."|null,
46
+ # "convId": "..."|null, "date": "..."|null,
47
+ # "platformHint": "..."|null }
48
+ # ],
49
+ # "redacted": <bool>, "degraded": <bool>, "degradeReason": "..."|null
50
+ # }
51
+ #
52
+ # Exit codes:
53
+ # 0 success
54
+ # 4 bad usage (no source, unknown flag, unreadable file)
55
+ # 5 format degrade (xlsx requested but openpyxl is not installed; the
56
+ # degraded JSON on stdout tells the user to export as CSV and re-run)
57
+
58
+ set -euo pipefail
59
+
60
+ FILE=""
61
+ USE_STDIN="false"
62
+ FORMAT="auto"
63
+ REDACT="true"
64
+
65
+ while [ $# -gt 0 ]; do
66
+ case "$1" in
67
+ --file)
68
+ [ $# -ge 2 ] || { echo "ERR: --file needs a value" >&2; exit 4; }
69
+ FILE="$2"; shift 2 ;;
70
+ --stdin) USE_STDIN="true"; shift ;;
71
+ --format)
72
+ [ $# -ge 2 ] || { echo "ERR: --format needs a value" >&2; exit 4; }
73
+ FORMAT="$2"; shift 2 ;;
74
+ --no-redact) REDACT="false"; shift ;;
75
+ -h|--help)
76
+ echo "usage: $0 --file <path> [--format csv|xlsx|txt|json|auto] [--no-redact] | --stdin [--format text]" >&2
77
+ exit 4 ;;
78
+ *)
79
+ echo "ERR: unexpected arg $1" >&2; exit 4 ;;
80
+ esac
81
+ done
82
+
83
+ if [ -z "$FILE" ] && [ "$USE_STDIN" != "true" ]; then
84
+ echo "ERR: no source (pass --file <path> or --stdin)" >&2
85
+ exit 4
86
+ fi
87
+ if [ -n "$FILE" ] && [ "$USE_STDIN" = "true" ]; then
88
+ echo "ERR: pass either --file or --stdin, not both" >&2
89
+ exit 4
90
+ fi
91
+ if [ -n "$FILE" ] && [ ! -r "$FILE" ]; then
92
+ echo "ERR: file not readable: $FILE" >&2
93
+ exit 4
94
+ fi
95
+
96
+ if [ "$FORMAT" = "auto" ]; then
97
+ if [ "$USE_STDIN" = "true" ]; then
98
+ FORMAT="text"
99
+ else
100
+ case "$FILE" in
101
+ *.csv) FORMAT="csv" ;;
102
+ *.xlsx) FORMAT="xlsx" ;;
103
+ *.json) FORMAT="json" ;;
104
+ *.txt|*.md) FORMAT="txt" ;;
105
+ *)
106
+ echo "ERR: cannot infer format from extension; pass --format" >&2
107
+ exit 4 ;;
108
+ esac
109
+ fi
110
+ fi
111
+
112
+ case "$FORMAT" in
113
+ csv|xlsx|txt|json|text) : ;;
114
+ *) echo "ERR: unknown format $FORMAT" >&2; exit 4 ;;
115
+ esac
116
+
117
+ STDIN_PAYLOAD=""
118
+ if [ "$USE_STDIN" = "true" ]; then
119
+ STDIN_PAYLOAD=$(cat)
120
+ fi
121
+
122
+ PC_FILE="$FILE" PC_FORMAT="$FORMAT" PC_REDACT="$REDACT" PC_STDIN="$STDIN_PAYLOAD" \
123
+ python3 - <<'PY'
124
+ import csv, datetime, io, json, os, re, sys
125
+
126
+ fmt = os.environ["PC_FORMAT"]
127
+ path = os.environ.get("PC_FILE") or ""
128
+ redact_on = os.environ.get("PC_REDACT") == "true"
129
+
130
+ # Same label set as context-link-extractor.sh GRAYLOG_RE.
131
+ ID_LABEL = re.compile(
132
+ r"(?P<label>x-conversationId|conversationId|convId|trxId|trx|transaction[ _-]?id)"
133
+ r"\s*[:=#]?\s*(?P<value>[A-Za-z0-9][A-Za-z0-9._-]{5,63})",
134
+ re.IGNORECASE,
135
+ )
136
+
137
+ def extract_ids(text):
138
+ trx, conv = None, None
139
+ for m in ID_LABEL.finditer(text or ""):
140
+ label = m.group("label").lower()
141
+ value = m.group("value")
142
+ if "conv" in label:
143
+ conv = conv or value
144
+ else:
145
+ trx = trx or value
146
+ return trx, conv
147
+
148
+ # Order matters: card (13-19 digits, separators allowed) before national-id
149
+ # (exactly 11) before phone (9+); a 16-digit card must not survive as a
150
+ # "phone". PNR-like runs last so redacted placeholders are not re-matched.
151
+ REDACTIONS = [
152
+ (re.compile(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}"), "[REDACTED:email]"),
153
+ (re.compile(r"\b\d(?:[ -]?\d){12,18}\b"), "[REDACTED:card]"),
154
+ (re.compile(r"\b\d{11}\b"), "[REDACTED:national-id]"),
155
+ (re.compile(r"(?<![\w.-])\+?\d(?:[ ()-]?\d){8,14}\b"), "[REDACTED:phone]"),
156
+ (re.compile(r"\b(?=[A-Z0-9]{6}\b)(?=[A-Z0-9]*[A-Z])(?=[A-Z0-9]*\d)[A-Z0-9]{6}\b"), "[REDACTED:pnr]"),
157
+ # All-letter PNRs exist too, but a bare 6-letter uppercase word is far too
158
+ # common to redact blind - only take one that follows a PNR-ish keyword.
159
+ (
160
+ re.compile(r"\b(?P<kw>pnr|rezervasyon kodu|booking (?:ref|reference|code))(?P<sep>[^A-Za-z0-9\n]{0,3})[A-Z]{6}\b", re.IGNORECASE),
161
+ lambda m: m.group("kw") + m.group("sep") + "[REDACTED:pnr]",
162
+ ),
163
+ ]
164
+
165
+ def redact(text):
166
+ if not redact_on:
167
+ return text
168
+ for rx, repl in REDACTIONS:
169
+ text = rx.sub(repl, text)
170
+ return text
171
+
172
+ TEXT_KEYS = ("comment", "complaint", "text", "description", "message", "yorum", "sikayet")
173
+ TRX_KEYS = re.compile(r"^(trxid|trx|transaction[ _-]?id)$", re.IGNORECASE)
174
+ CONV_KEYS = re.compile(r"^(convid|conversationid|x-conversationid)$", re.IGNORECASE)
175
+ DATE_KEYS = re.compile(r"^(date|created|createdat|timestamp)$", re.IGNORECASE)
176
+ PLATFORM_KEYS = re.compile(r"^(platform|channel|os|device)$", re.IGNORECASE)
177
+
178
+ def row_to_complaint(row):
179
+ text_val, trx, conv, date, platform = None, None, None, None, None
180
+ longest = ""
181
+ for key, raw in row.items():
182
+ if key is None or raw is None:
183
+ continue
184
+ val = str(raw).strip()
185
+ if not val:
186
+ continue
187
+ k = str(key).strip()
188
+ if TRX_KEYS.match(k):
189
+ trx = val
190
+ elif CONV_KEYS.match(k):
191
+ conv = val
192
+ elif DATE_KEYS.match(k):
193
+ date = val
194
+ elif PLATFORM_KEYS.match(k):
195
+ platform = val
196
+ elif k.lower() in TEXT_KEYS:
197
+ text_val = val
198
+ elif len(val) > len(longest):
199
+ longest = val
200
+ text = text_val or longest
201
+ if not text:
202
+ return None
203
+ found_trx, found_conv = extract_ids(text)
204
+ return {
205
+ "text": text,
206
+ "trxId": trx or found_trx,
207
+ "convId": conv or found_conv,
208
+ "date": date,
209
+ "platformHint": platform,
210
+ }
211
+
212
+ def blocks_from_text(payload):
213
+ payload = payload.strip()
214
+ if not payload:
215
+ return []
216
+ blocks = [b.strip() for b in re.split(r"\n\s*\n", payload) if b.strip()]
217
+ if len(blocks) == 1 and re.search(r"^\s*(?:\d+[.)]|-)\s+", payload, re.MULTILINE):
218
+ blocks = [
219
+ b.strip()
220
+ for b in re.split(r"^\s*(?:\d+[.)]|-)\s+", payload, flags=re.MULTILINE)
221
+ if b.strip()
222
+ ]
223
+ return blocks
224
+
225
+ degraded = False
226
+ degrade_reason = None
227
+ raw_complaints = []
228
+
229
+ if fmt == "text":
230
+ for block in blocks_from_text(os.environ.get("PC_STDIN") or ""):
231
+ trx, conv = extract_ids(block)
232
+ raw_complaints.append(
233
+ {"text": block, "trxId": trx, "convId": conv, "date": None, "platformHint": None}
234
+ )
235
+ elif fmt == "txt":
236
+ with open(path, encoding="utf-8-sig", errors="replace") as f:
237
+ payload = f.read()
238
+ for block in blocks_from_text(payload):
239
+ trx, conv = extract_ids(block)
240
+ raw_complaints.append(
241
+ {"text": block, "trxId": trx, "convId": conv, "date": None, "platformHint": None}
242
+ )
243
+ elif fmt == "csv":
244
+ with open(path, encoding="utf-8-sig", errors="replace", newline="") as f:
245
+ sample = f.read(4096)
246
+ f.seek(0)
247
+ try:
248
+ dialect = csv.Sniffer().sniff(sample, delimiters=",;\t")
249
+ except csv.Error:
250
+ dialect = csv.excel
251
+ for row in csv.DictReader(f, dialect=dialect):
252
+ c = row_to_complaint(row)
253
+ if c:
254
+ raw_complaints.append(c)
255
+ elif fmt == "json":
256
+ with open(path, encoding="utf-8-sig") as f:
257
+ data = json.load(f)
258
+ if isinstance(data, dict):
259
+ data = data.get("complaints") or data.get("items") or []
260
+ if not isinstance(data, list):
261
+ data = []
262
+ for row in data:
263
+ if isinstance(row, str):
264
+ trx, conv = extract_ids(row)
265
+ raw_complaints.append(
266
+ {"text": row.strip(), "trxId": trx, "convId": conv, "date": None, "platformHint": None}
267
+ )
268
+ elif isinstance(row, dict):
269
+ c = row_to_complaint(row)
270
+ if c:
271
+ raw_complaints.append(c)
272
+ elif fmt == "xlsx":
273
+ try:
274
+ import openpyxl
275
+ except ImportError:
276
+ degraded = True
277
+ degrade_reason = "openpyxl not installed; export the sheet as CSV and re-run with --format csv"
278
+ if not degraded:
279
+ wb = openpyxl.load_workbook(path, read_only=True, data_only=True)
280
+ ws = wb.active
281
+ rows = ws.iter_rows(values_only=True)
282
+ header = next(rows, None)
283
+ if header:
284
+ keys = [str(h).strip() if h is not None else "" for h in header]
285
+ for values in rows:
286
+ row = {k: v for k, v in zip(keys, values) if k}
287
+ c = row_to_complaint(row)
288
+ if c:
289
+ raw_complaints.append(c)
290
+ wb.close()
291
+
292
+ complaints = []
293
+ for i, c in enumerate(raw_complaints, start=1):
294
+ complaints.append(
295
+ {
296
+ "id": "C-%02d" % i,
297
+ "text": redact(c["text"]),
298
+ "trxId": c["trxId"],
299
+ "convId": c["convId"],
300
+ "date": c["date"],
301
+ "platformHint": (c["platformHint"] or None) and str(c["platformHint"]).strip().lower(),
302
+ }
303
+ )
304
+
305
+ result = {
306
+ "parsedAt": datetime.datetime.utcnow().isoformat() + "Z",
307
+ "source": path or "stdin",
308
+ "format": fmt,
309
+ "complaints": complaints,
310
+ "redacted": redact_on,
311
+ "degraded": degraded,
312
+ "degradeReason": degrade_reason,
313
+ }
314
+ print(json.dumps(result, ensure_ascii=False))
315
+ sys.exit(5 if degraded else 0)
316
+ PY
@@ -34,9 +34,9 @@ The selected scope routes to a platform skill:
34
34
 
35
35
  | Platform | Skill | Notes |
36
36
  |---|---|---|
37
- | iOS / SwiftUI | `ai-ios-engineering-toolkit:figma-component-wiki` | Reads `Configuration` + `+Modifiers` from the source, captures Figma screenshots via MCP / REST, runs Snapshot tests for iOS dark/light/RTL. |
38
- | Android / Compose | `ai-android-engineering-toolkit:figma-component-wiki` | Reads Compose composables + previews, runs Paparazzi screenshots if available, otherwise generates from layout inspector. |
39
- | Generic | `ai-ios-engineering-toolkit:figma-component-wiki` (fallback) | Used when `state.platform` is unknown (fallback path - typically a flag). |
37
+ | iOS / SwiftUI | `ai-ios-toolkit:figma-component-wiki` | Reads `Configuration` + `+Modifiers` from the source, captures Figma screenshots via MCP / REST, runs Snapshot tests for iOS dark/light/RTL. |
38
+ | Android / Compose | `ai-android-toolkit:figma-component-wiki` | Reads Compose composables + previews, runs Paparazzi screenshots if available, otherwise generates from layout inspector. |
39
+ | Generic | `ai-ios-toolkit:figma-component-wiki` (fallback) | Used when `state.platform` is unknown (fallback path - typically a flag). |
40
40
 
41
41
  Full per-skill contract: `$HOME/.claude/multi-agent-refs/wiki-capture.md`.
42
42
 
@@ -0,0 +1,99 @@
1
+ # Complaint-Analysis Report Template
2
+
3
+ Master layout for the report `/multi-agent:complaint-analysis` renders at Phase 4 and dispatches at Phase 5. Single-language body: every heading and prose block is written in `outputLanguage` (`tr` or `en`); the TR / EN heading pairs below show both forms, render ONE. Verdict tokens, Graylog excerpts, and field names in routing entries stay English (SKILL Locked 9). The deterministic gate over an emitted instance is `scripts/validate-complaint-doc.mjs` - keep this file and that validator in sync.
4
+
5
+ Numbering is fixed 1..8. Section 4 (routing) is rendered only when at least one `core` verdict exists; numbering still re-flows sequentially when it is omitted (same omission style as the analysis template).
6
+
7
+ ## YAML front-matter (required keys)
8
+
9
+ ```yaml
10
+ ---
11
+ run_name: complaints-20260810
12
+ generated_at: <UTC ISO8601>
13
+ language: tr | en
14
+ complaint_count: <N>
15
+ repos: "<name>:<layer>, <name>:<layer>, ..."
16
+ verdict_counts: "client=<X> bff=<Y> core=<Z> insufficient=<W>"
17
+ graylog_degraded: true | false
18
+ ---
19
+ ```
20
+
21
+ `run_name`, `generated_at`, `language`, `complaint_count`, `graylog_degraded` are validator-required; `repos` and `verdict_counts` are part of the template contract but advisory to the gate.
22
+
23
+ ## 1. Özet / Summary
24
+
25
+ 2-4 sentences: how many complaints, where they came from (source types only - never file contents), the verdict distribution, and whether Graylog was degraded for any of them. Follow with the count table:
26
+
27
+ ```
28
+ | Verdict | Count |
29
+ |---|---|
30
+ | client | X |
31
+ | bff | Y |
32
+ | core | Z |
33
+ | insufficient-evidence | W |
34
+ ```
35
+
36
+ Then the coverage table - every selected repo appears, including the ones that came back clean, so "only the client layer needed a fix" is visible at a glance:
37
+
38
+ ```
39
+ | Repo | Layer | Evidence hits | Verdicts attributed |
40
+ |---|---|---|---|
41
+ | <owner>/my-ios-app | ios | 3 | C-01 |
42
+ | <owner>/my-mobile-bff | mobile-bff | 0 | - (incelendi, temiz / checked, clean) |
43
+ ```
44
+
45
+ ## 2. Triage Tablosu / Triage Table
46
+
47
+ One row per complaint. Verdict tokens are the English enum: `client:ios`, `client:android`, `client:web`, `bff:mobile-bff`, `bff:web-bff`, `core`, `insufficient-evidence`. The validator requires every row in this section whose FIRST cell is a `C-NN` id to carry one of these tokens in a cell of its own (cell-exact; the Section 1 coverage table citing ids in its last column is exempt by scope).
48
+
49
+ ```
50
+ | Id | Özet (redakte) | trx/conv | Verdict | Confidence | Routing |
51
+ |---|---|---|---|---|---|
52
+ | C-01 | <redacted excerpt <= 80 chars> | trx-9f3a | client:ios | high | - |
53
+ | C-02 | ... | conv-77aa | core | medium | payment-core |
54
+ ```
55
+
56
+ `Routing` column: the suspected core service for `core` rows, `-` otherwise.
57
+
58
+ ## 3. Şikayet Detayları / Complaint Details
59
+
60
+ One `### C-NN` subsection per complaint:
61
+
62
+ - **Şikayet / Complaint**: the redacted text (full, not the excerpt).
63
+ - **Graylog**: status line (`ok: N messages` / `degraded: <reason>` / `skipped: user choice`), then up to 3 evidence lines, English, shape `timestamp source level message-excerpt`. Redact any PII the log itself carries before quoting.
64
+ - **Repo kanıtı / Repo evidence**: `file:line - signal (matchKind)` rows from the correlation phase; `-` when none.
65
+ - **Elenen katmanlar / Ruled out**: one line listing the repos/layers that were grepped for this complaint's signals and returned nothing (e.g. `mobile-bff, web-bff: sinyal yok`). This is what justifies a single-layer verdict.
66
+ - **Verdict + gerekçe / rationale**: the verdict token, confidence, and 1-3 sentences citing the evidence above (SKILL Locked 10: client/bff needs 1 Graylog + 1 repo citation; core needs the Graylog line naming the upstream).
67
+ - **Geliştirme planı / Fix plan** (client/bff only, SKILL Locked 13; validator-required for every client/bff verdict): 2-5 numbered steps grounded in the existing architecture - each step references a cited `file:line` or component, reuse-first, tests included. Never rendered for core / insufficient-evidence.
68
+ - **Dev prompt** (client/bff only): one fenced English block ready to paste into `/multi-agent:dev` or a Jira description - complaint summary, root cause, cited files, fix plan steps, acceptance check, and the `[C-NN]` id.
69
+
70
+ ## 4. Core Yönlendirme Önerileri / Core Routing Recommendations
71
+
72
+ Rendered only when `core` verdicts exist. One `### C-NN` subsection per core verdict (the validator checks each core id appears here):
73
+
74
+ ```
75
+ suspectedService: <Graylog source>
76
+ endpoint: <path or ->
77
+ errorCode: <code or ->
78
+ evidenceExcerpt: <short redacted EN excerpt>
79
+ suggestedQueue: <prefs.projects[*].routing.coreTeamLabel or ->
80
+ confidence: high | medium | low
81
+ ```
82
+
83
+ No fix analysis, no code speculation about the core service (SKILL Locked 5). This block is what gets forwarded to the core team.
84
+
85
+ ## 5. Açık Sorular / Open Questions
86
+
87
+ One bullet per `insufficient-evidence` verdict (what is missing: id, reproducible log, repo signal) plus any ambiguity worth a human decision (e.g. client-vs-bff attribution at low confidence).
88
+
89
+ ## 6. Metodoloji / Methodology
90
+
91
+ Fixed short block: source types ingested, redaction applied (`parse-complaints.sh`), Graylog query window/limit actually used, repos + layers grepped, and a **degraded services** list (`graylog: <reason>` when any fetch degraded - a degraded payload is never silently dropped).
92
+
93
+ ## 7. Referanslar / References
94
+
95
+ Input source names only - file basename + row count, Jira issue keys, Confluence page titles/URLs. Never inline the input contents. Then the repo list with layers.
96
+
97
+ ## 8. (Footer)
98
+
99
+ One line: `Generated by /multi-agent:complaint-analysis - report only, no branches or commits.` (localized).
@@ -1,6 +1,6 @@
1
1
  # Component Dispatch (Phase 3 short-circuit)
2
2
 
3
- > **TLDR** - When `taskType === "component"` (Figma URL in task description or instruction-driven figma workflow), multi-agent Phase 3 **does not run the TDD loop**. It delegates the entire phase to the enabled `ai-<platform>-engineering-toolkit` **marketplace plugin's** component skill (`create-component`, falling back to `create-ui-component`) via the Skill tool. Implementation lives in the plugin; multi-agent's job is classification, dispatch, and state report. The pipeline no longer bundles its own `figma-to-component` orchestrator - component skills live in one place, the plugin marketplace.
3
+ > **TLDR** - When `taskType === "component"` (Figma URL in task description or instruction-driven figma workflow), multi-agent Phase 3 **does not run the TDD loop**. It delegates the entire phase to the enabled `ai-<platform>-toolkit` **marketplace plugin's** component skill (`create-component`, falling back to `create-ui-component`) via the Skill tool. Implementation lives in the plugin; multi-agent's job is classification, dispatch, and state report. The pipeline no longer bundles its own `figma-to-component` orchestrator - component skills live in one place, the plugin marketplace.
4
4
 
5
5
  This doc is referenced from `$HOME/.claude/multi-agent-refs/phases/phase-3-dev.md`. Keeping it separate lets `phase-3-dev.md` remain tight (it's already the largest phase doc) and gives the orchestrator-report contract a stable URL for both Claude-side and Copilot-side implementations.
6
6
 
@@ -37,8 +37,8 @@ run produced entities and a mapper but left the screen half-wired.
37
37
 
38
38
  | `state.componentScope` | Meaning | iOS skill | Android skill |
39
39
  |---|---|---|---|
40
- | `screen` (default when the frame is a full screen, or the task names a screen) | Full clean-architecture vertical: Entity → Repository → Mapper → UseCase → LocalizedText → AnalyticsTracking → CoordinatorEvent → ViewModel → Scene → Preview, then verify | `ai-ios-engineering-toolkit:create-screen` | `ai-android-engineering-toolkit:create-screen` |
41
- | `component` | One reusable UI component (Configuration / View / +Modifiers / Code Connect) | `ai-ios-engineering-toolkit:create-component` (fallback `create-ui-component`) | `ai-android-engineering-toolkit:create-component` (fallback `create-ui-component`) |
40
+ | `screen` (default when the frame is a full screen, or the task names a screen) | Full clean-architecture vertical: Entity → Repository → Mapper → UseCase → LocalizedText → AnalyticsTracking → CoordinatorEvent → ViewModel → Scene → Preview, then verify | `ai-ios-toolkit:create-screen` | `ai-android-toolkit:create-screen` |
41
+ | `component` | One reusable UI component (Configuration / View / +Modifiers / Code Connect) | `ai-ios-toolkit:create-component` (fallback `create-ui-component`) | `ai-android-toolkit:create-component` (fallback `create-ui-component`) |
42
42
  | `evolve` | Change an existing component | `evolve-component` (fallback `evolve-ui-component`) | same |
43
43
 
44
44
  ```
@@ -52,7 +52,7 @@ a single atom is `component`. When it cannot be decided, ask - do not default
52
52
  omits the wiring.
53
53
 
54
54
  **Pre-implementation validation is not optional on iOS.** Before the create skill
55
- runs, dispatch `ai-ios-engineering-toolkit:figma-validate` for the frame. It checks
55
+ runs, dispatch `ai-ios-toolkit:figma-validate` for the frame. It checks
56
56
  registry presence, Code Connect strategy, **design token compliance**, dependency
57
57
  readiness, atomic scope and already-implemented status in about ten seconds. Those
58
58
  are precisely the checks whose absence produced guessed spacing and an unpublished
@@ -60,7 +60,7 @@ Code Connect binding. A `figma-validate` failure halts the dispatch.
60
60
 
61
61
  **Dual-name resolution.** The public (`multi-agent-plugins`) and a corporate/private marketplace named the same skill differently - `create-component` vs `create-ui-component`. Dispatch tries `create-component` first; if it is not available in the current repo, tries `create-ui-component`. (Same dual-name rule applies when `taskType` maps to evolve → `evolve-component`/`evolve-ui-component`, or fix → `fix-bug`.)
62
62
 
63
- If **neither** resolves, the platform's `ai-<platform>-engineering-toolkit` plugin is not enabled in this repo. **Halt with a user-visible error**: "component task requires the ai-<platform>-engineering-toolkit plugin enabled in this repo (`.claude/settings.local.json`)." Do not silently fall back to TDD - a component task ran through the bugfix path would produce wrong artefacts.
63
+ If **neither** resolves, the platform's `ai-<platform>-toolkit` plugin is not enabled in this repo. **Halt with a user-visible error**: "component task requires the ai-<platform>-toolkit plugin enabled in this repo (`.claude/settings.local.json`)." Do not silently fall back to TDD - a component task ran through the bugfix path would produce wrong artefacts.
64
64
 
65
65
  ## Dispatch call
66
66
 
@@ -119,7 +119,7 @@ Phase 4 runs in `--dev` as it does in the full pipeline, and its reviewer count
119
119
 
120
120
  Component dispatch is **no longer byte-identical across CLIs** and that is by design (see `cross-cli-contract.md` section 1.1):
121
121
 
122
- - **Claude Code**: dispatches to the enabled `ai-<platform>-engineering-toolkit` marketplace plugin via the Skill tool (this doc).
122
+ - **Claude Code**: dispatches to the enabled `ai-<platform>-toolkit` marketplace plugin via the Skill tool (this doc).
123
123
  - **Copilot CLI**: has no plugin loader; it continues to use its standalone `~/.copilot/skills/figma-*` skill copies (frozen fallback). Copilot's resolution + progress lines follow those local skills.
124
124
 
125
125
  What still MUST match across CLIs: the `taskType === "component"` classification, the `state.phases["3"].subphases[]` shape the dispatch layer writes, and `--dev` elision semantics. Figma-skill *inventory* parity is no longer enforced. `smoke-cross-cli-behavior.sh` asserts only the classification + state-shape axis for components.