@mmerterden/multi-agent-pipeline 14.2.1 → 15.0.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 (184) hide show
  1. package/CHANGELOG.md +118 -6
  2. package/README.md +20 -9
  3. package/README.tr.md +150 -0
  4. package/docs/FIGMA_PIPELINE.md +3 -3
  5. package/docs/adr/0006-skills-core-external-split.md +1 -1
  6. package/docs/adr/0009-claude-stack-skills-plugin-only.md +31 -0
  7. package/docs/adr/README.md +1 -0
  8. package/docs/architecture.md +24 -9
  9. package/docs/ecosystem.md +237 -0
  10. package/docs/features.md +5 -5
  11. package/index.js +2 -0
  12. package/install/_codex-agents.mjs +11 -2
  13. package/install/_common.mjs +65 -1
  14. package/install/_dev-only-files.mjs +0 -1
  15. package/install/_platform-filter.mjs +73 -7
  16. package/install/_plugin-skills.mjs +33 -8
  17. package/install/claude.mjs +144 -59
  18. package/install/codex.mjs +37 -7
  19. package/install/copilot.mjs +36 -11
  20. package/install/index.mjs +6 -2
  21. package/install/templates/codex-instructions.md +1 -1
  22. package/install/templates/copilot-instructions.md +15 -12
  23. package/package.json +1 -2
  24. package/pipeline/commands/multi-agent/SKILL.md +4 -2
  25. package/pipeline/commands/multi-agent/analysis/SKILL.md +5 -5
  26. package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +2 -2
  27. package/pipeline/commands/multi-agent/autopilot/SKILL.md +6 -2
  28. package/pipeline/commands/multi-agent/build-optimize/SKILL.md +9 -9
  29. package/pipeline/commands/multi-agent/channels/SKILL.md +16 -5
  30. package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +186 -0
  31. package/pipeline/commands/multi-agent/create-jira/SKILL.md +4 -4
  32. package/pipeline/commands/multi-agent/dev/SKILL.md +10 -23
  33. package/pipeline/commands/multi-agent/dev-autopilot/SKILL.md +10 -2
  34. package/pipeline/commands/multi-agent/dev-local/SKILL.md +10 -24
  35. package/pipeline/commands/multi-agent/dev-local-autopilot/SKILL.md +10 -3
  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 +13 -2
  40. package/pipeline/commands/multi-agent/language/SKILL.md +1 -1
  41. package/pipeline/commands/multi-agent/local/SKILL.md +6 -2
  42. package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +6 -2
  43. package/pipeline/commands/multi-agent/log/SKILL.md +7 -1
  44. package/pipeline/commands/multi-agent/prune-prompts/SKILL.md +81 -0
  45. package/pipeline/commands/multi-agent/resume/SKILL.md +1 -1
  46. package/pipeline/commands/multi-agent/{ship → resume-local}/SKILL.md +13 -9
  47. package/pipeline/commands/multi-agent/setup/SKILL.md +5 -5
  48. package/pipeline/commands/multi-agent/stack/SKILL.md +55 -43
  49. package/pipeline/commands/multi-agent/store-ready/SKILL.md +3 -3
  50. package/pipeline/commands/multi-agent/sync/SKILL.md +18 -11
  51. package/pipeline/commands/multi-agent/testflight-validation/SKILL.md +1 -1
  52. package/pipeline/commands/multi-agent/uninstall/SKILL.md +2 -0
  53. package/pipeline/commands/multi-agent/update/SKILL.md +1 -1
  54. package/pipeline/lib/extract-conventions.sh +44 -15
  55. package/pipeline/lib/fetch-figma-annotations.sh +8 -1
  56. package/pipeline/lib/fetch-fortify.sh +23 -8
  57. package/pipeline/lib/figma-screenshot.sh +11 -1
  58. package/pipeline/lib/issue-fetcher.sh +77 -10
  59. package/pipeline/lib/md2confluence-v3.py +16 -2
  60. package/pipeline/lib/parse-complaints.sh +306 -0
  61. package/pipeline/lib/plan-todos.sh +5 -2
  62. package/pipeline/lib/post-pr-review.sh +8 -6
  63. package/pipeline/lib/shadow-git.sh +50 -9
  64. package/pipeline/lib/submodule-detector.sh +8 -1
  65. package/pipeline/multi-agent-refs/_input-parser.md +1 -1
  66. package/pipeline/multi-agent-refs/channels/confluence.md +3 -0
  67. package/pipeline/multi-agent-refs/channels/issue-comment.md +2 -2
  68. package/pipeline/multi-agent-refs/channels/jira.md +13 -2
  69. package/pipeline/multi-agent-refs/channels/pr-review-actions.md +1 -1
  70. package/pipeline/multi-agent-refs/channels/pr.md +20 -0
  71. package/pipeline/multi-agent-refs/channels/wiki.md +4 -4
  72. package/pipeline/multi-agent-refs/complaint-analysis-template.md +99 -0
  73. package/pipeline/multi-agent-refs/component-dispatch.md +6 -6
  74. package/pipeline/multi-agent-refs/cross-cli-contract.md +16 -16
  75. package/pipeline/multi-agent-refs/features/external-context-injection.md +1 -1
  76. package/pipeline/multi-agent-refs/features/stack-skill-routing.md +5 -5
  77. package/pipeline/multi-agent-refs/features/worktree-finalize.md +1 -1
  78. package/pipeline/multi-agent-refs/generate-issue.md +3 -3
  79. package/pipeline/multi-agent-refs/issue-jira-triad.md +3 -3
  80. package/pipeline/multi-agent-refs/payload-contracts.md +67 -0
  81. package/pipeline/multi-agent-refs/phases/modes.md +20 -0
  82. package/pipeline/multi-agent-refs/phases/phase-0-init.md +2 -2
  83. package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +7 -7
  84. package/pipeline/multi-agent-refs/phases/phase-2-planning.md +5 -5
  85. package/pipeline/multi-agent-refs/phases/phase-3-dev.md +3 -3
  86. package/pipeline/multi-agent-refs/phases/phase-4-review.md +12 -12
  87. package/pipeline/multi-agent-refs/phases/phase-5-test.md +1 -1
  88. package/pipeline/multi-agent-refs/phases/phase-6-commit.md +8 -40
  89. package/pipeline/multi-agent-refs/phases/phase-7-report.md +5 -3
  90. package/pipeline/multi-agent-refs/phases.md +6 -0
  91. package/pipeline/multi-agent-refs/rules.md +2 -0
  92. package/pipeline/multi-agent-refs/tracker-contract.md +1 -1
  93. package/pipeline/multi-agent-refs/wiki-capture.md +2 -2
  94. package/pipeline/preferences-template.json +13 -5
  95. package/pipeline/rules/figma-pipeline.md +2 -2
  96. package/pipeline/schemas/agent-state.schema.json +1 -1
  97. package/pipeline/schemas/complaint-analysis-spec.schema.json +216 -0
  98. package/pipeline/schemas/migrations/prefs-2.5.0-to-2.6.0.mjs +46 -0
  99. package/pipeline/schemas/prefs.schema.json +277 -67
  100. package/pipeline/schemas/token-budget.json +2 -2
  101. package/pipeline/scripts/_stack-routing.mjs +79 -0
  102. package/pipeline/scripts/audit-log-rotate.sh +14 -1
  103. package/pipeline/scripts/build-skills-index.mjs +11 -0
  104. package/pipeline/scripts/build-stack-plugins.mjs +35 -60
  105. package/pipeline/scripts/check-derived-drift.mjs +65 -29
  106. package/pipeline/scripts/diff-explain.mjs +41 -3
  107. package/pipeline/scripts/diff-risk-score.mjs +72 -8
  108. package/pipeline/scripts/gc-worktrees.sh +4 -1
  109. package/pipeline/scripts/gen-mode-dispatch.mjs +1 -1
  110. package/pipeline/scripts/gen-skills-index.mjs +1 -1
  111. package/pipeline/scripts/learning-curve.mjs +8 -2
  112. package/pipeline/scripts/match-skills.mjs +8 -2
  113. package/pipeline/scripts/migrate-prefs.mjs +28 -20
  114. package/pipeline/scripts/output-quality-check.sh +15 -4
  115. package/pipeline/scripts/phase-tracker.sh +33 -12
  116. package/pipeline/scripts/phase0-exit-gate.mjs +3 -2
  117. package/pipeline/scripts/pre-commit-check.sh +69 -22
  118. package/pipeline/scripts/render-agent-log-cost.sh +8 -3
  119. package/pipeline/scripts/render-cost-summary.sh +42 -22
  120. package/pipeline/scripts/render-work-summary.sh +47 -13
  121. package/pipeline/scripts/review-scope.mjs +1 -1
  122. package/pipeline/scripts/run-aggregator.mjs +43 -14
  123. package/pipeline/scripts/scan-agent-config.sh +1 -1
  124. package/pipeline/scripts/skill-conformance.mjs +165 -30
  125. package/pipeline/scripts/smoke-cross-cli-behavior.sh +1 -1
  126. package/pipeline/scripts/smoke-schema-validation.sh +5 -1
  127. package/pipeline/scripts/test-gap-rules/android.json +25 -0
  128. package/pipeline/scripts/test-gap-rules/ios.json +34 -0
  129. package/pipeline/scripts/test-gap-rules/node.json +29 -0
  130. package/pipeline/scripts/test-gap-rules/python.json +25 -0
  131. package/pipeline/scripts/test-gap-scan.mjs +45 -6
  132. package/pipeline/scripts/uninstall.mjs +196 -14
  133. package/pipeline/scripts/update-issue-progress.sh +12 -16
  134. package/pipeline/scripts/validate-complaint-doc.mjs +229 -0
  135. package/pipeline/scripts/validate-reviewer.mjs +9 -3
  136. package/pipeline/scripts/worktree-finalize.sh +23 -2
  137. package/pipeline/skills/.skill-manifest.json +156 -108
  138. package/pipeline/skills/.skills-index.json +459 -13
  139. package/pipeline/skills/shared/README.md +15 -11
  140. package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +1 -1
  141. package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +4 -0
  142. package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
  143. package/pipeline/skills/shared/core/multi-agent-complaint-analysis/SKILL.md +49 -0
  144. package/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md +1 -1
  145. package/pipeline/skills/shared/core/multi-agent-dev/SKILL.md +4 -17
  146. package/pipeline/skills/shared/core/multi-agent-dev-autopilot/SKILL.md +8 -0
  147. package/pipeline/skills/shared/core/multi-agent-dev-local/SKILL.md +5 -18
  148. package/pipeline/skills/shared/core/multi-agent-dev-local-autopilot/SKILL.md +8 -0
  149. package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +2 -2
  150. package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +1 -1
  151. package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +4 -0
  152. package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +4 -0
  153. package/pipeline/skills/shared/core/multi-agent-prune-prompts/SKILL.md +83 -0
  154. package/pipeline/skills/shared/core/{multi-agent-ship → multi-agent-resume-local}/SKILL.md +10 -6
  155. package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +79 -22
  156. package/pipeline/skills/shared/core/multi-agent-store-ready/SKILL.md +1 -1
  157. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +8 -8
  158. package/pipeline/skills/shared/core/multi-agent-testflight-validation/SKILL.md +1 -1
  159. package/pipeline/skills/shared/external/ios-coding-standard/modules/_TEMPLATE.yml +2 -2
  160. package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +368 -33
  161. package/pipeline/skills/shared/external/ios-coding-standard/references/swiftlint.draft.yml +1 -2
  162. package/pipeline/skills/shared/external/ios-coding-standard/scripts/check_structure.py +765 -0
  163. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +75 -0
  164. package/pipeline/skills/shared/external/ios-module-structure/modules/_TEMPLATE.yml +131 -0
  165. package/pipeline/skills/shared/external/ios-module-structure/references/rules.yml +559 -0
  166. package/pipeline/skills/shared/external/ios-module-structure/scripts/check_structure.py +765 -0
  167. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +302 -0
  168. package/pipeline/skills/shared/external/localization-reuse-map/example-mapping.json +187 -0
  169. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +156 -0
  170. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +108 -0
  171. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +176 -0
  172. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +865 -0
  173. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +335 -0
  174. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +344 -0
  175. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +130 -0
  176. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +264 -0
  177. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +298 -0
  178. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +529 -0
  179. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +187 -0
  180. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +171 -0
  181. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +184 -0
  182. package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +26 -0
  183. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +173 -0
  184. package/pipeline/skills/skills-index.md +9 -5
@@ -0,0 +1,237 @@
1
+ # Ecosystem: Pipeline × Marketplace Plugins × Dev-Toolkit MCP
2
+
3
+ This pipeline is not one repo. It's three, each owned separately, each versioned
4
+ separately, wired together at install time and at run time:
5
+
6
+ | Repo | What it owns | Ships as |
7
+ |---|---|---|
8
+ | **`multi-agent-pipeline`** (this repo) | Orchestration: the 8-phase flow, the 50 slash commands, quality gates, review/triage, cross-CLI parity | npm package (`@mmerterden/multi-agent-pipeline`), installs itself onto Claude Code / Copilot CLI / Codex CLI |
9
+ | **`multi-agent-plugins`** | Stack knowledge: per-platform component/lifecycle skills (iOS, Android, Frontend, Backend) + shared knowledge | Claude Code marketplace, 5 independently-versioned plugins |
10
+ | **`dev-toolkit-mcp`** | The pipeline's hands on devices and browsers: 80 MCP tools across 6 categories (simulator/emulator control, accessibility audit, store compliance, web automation, Figma-vs-mock design audit, an agent-DSL batch runner) | npm package, registered as a standard stdio MCP server on every host |
11
+
12
+ None of the three depends on the others at the code level. They compose through two
13
+ narrow contracts: the **Skill tool** (pipeline → plugin, at Phase 3) and the **MCP
14
+ protocol** (pipeline skills → dev-toolkit, at Phase 5 / design-check / store-ready).
15
+ Either can be swapped or removed without touching the other two's source.
16
+
17
+ ```mermaid
18
+ graph LR
19
+ subgraph PIPE ["multi-agent-pipeline (orchestrator)"]
20
+ direction TB
21
+ PHASES["8 phases · 50 commands"]
22
+ GATES["deterministic gates + review triage"]
23
+ end
24
+
25
+ subgraph PLUG ["multi-agent-plugins (stack knowledge)"]
26
+ direction TB
27
+ IOSP["ai-ios-toolkit"]
28
+ ANDP["ai-android-toolkit"]
29
+ FEP["ai-frontend-toolkit"]
30
+ BEP["ai-backend-toolkit"]
31
+ COMP["ai-common-toolkit"]
32
+ end
33
+
34
+ subgraph DTK ["dev-toolkit-mcp (device/browser hands)"]
35
+ direction TB
36
+ DEV["Device Control (58)"]
37
+ A11Y["Accessibility Audit (2)"]
38
+ STORE["Store Compliance (5)"]
39
+ WEB["Web Automation (8)"]
40
+ DESIGN["Design Audit (6)"]
41
+ AGENTDSL["Agent DSL (1)"]
42
+ end
43
+
44
+ PHASES -->|"Phase 3: Skill tool<br/>taskType===component"| PLUG
45
+ PHASES -->|"Phase 5 / design-check /<br/>store-ready: MCP tool calls"| DTK
46
+ GATES -.->|"Phase 4 Security Auditor"| STORE
47
+
48
+ style PIPE fill:#ffd,stroke:#333
49
+ style PLUG fill:#dfd,stroke:#333
50
+ style DTK fill:#dff,stroke:#333
51
+ ```
52
+
53
+ ---
54
+
55
+ ## 1. Authoring flow: one source of truth, five sync targets
56
+
57
+ `~/.claude/` on the maintainer's machine is authoritative. Everything else is a
58
+ derived, synced, or independently-shipped artifact. `/multi-agent:sync` is the one
59
+ command that walks all five targets in order, detects which are stale, and updates
60
+ only those:
61
+
62
+ ```mermaid
63
+ graph TD
64
+ CC["Claude Code<br/>~/.claude/commands/multi-agent/<br/>(source of truth)"]
65
+
66
+ CC -->|"Step 2: copy + reformat<br/>50 sub-command skills"| COP["Copilot CLI<br/>~/.copilot/skills/"]
67
+ CC -->|"Step 2b: transform<br/>(install.js --codex)"| COD["Codex CLI<br/>1 router skill + 50 refs<br/>+ 8 agent TOML"]
68
+ CC -->|"Step 3: genericize<br/>(strip personal data)"| REPO["multi-agent-pipeline repo<br/>pipeline/"]
69
+ CC -->|"Step 4: version + feature sync"| WEB["Website<br/>projects.ts / i18n.tsx"]
70
+
71
+ REPO -->|"Step 3c: build-stack-plugins.mjs<br/>rebuilds knowledge/ from<br/>shared/external"| PLUGREPO["multi-agent-plugins repo<br/>(5 stack plugins)"]
72
+
73
+ DTK2["dev-toolkit-mcp repo<br/>(own codebase, own gates,<br/>NOT generated from Claude)"]
74
+ SYNC3D["Step 3d: detect movement →<br/>gate → commit → publish"]
75
+ CC -.->|"sync only SHIPS this,<br/>never authors it"| SYNC3D
76
+ SYNC3D -.-> DTK2
77
+
78
+ REPO -->|"npm publish"| NPM["GitHub Packages"]
79
+ WEB -->|"git push → auto-deploy"| VERCEL["Vercel"]
80
+ PLUGREPO -->|"git push"| MKT["Claude Code marketplace"]
81
+ DTK2 -->|"npm publish"| NPM2["GitHub Packages<br/>(private)"]
82
+
83
+ style CC fill:#f9f,stroke:#333
84
+ style DTK2 fill:#dff,stroke:#333,stroke-dasharray: 5 5
85
+ ```
86
+
87
+ **Why `dev-toolkit-mcp` is drawn differently.** The other four targets are *derived*
88
+ from the Claude Code source - sync writes their content. `dev-toolkit-mcp` is not:
89
+ it's a separate codebase developed on its own schedule. Sync's Step 3d only
90
+ *detects* whether it moved (dirty tree, unpushed commits, untagged version), runs
91
+ **its own** gate suite, and ships it - commit, tag, `npm publish`. If the pipeline
92
+ needs a tool that toolkit doesn't have yet, that's a two-repo change: add the tool
93
+ in `dev-toolkit-mcp`, ship it, then bump the minimum version pin back in
94
+ `cross-cli-contract.md` (see section 4).
95
+
96
+ **Also not generated: the plugins' own authored skills.** `build-stack-plugins.mjs`
97
+ only rebuilds each plugin's `knowledge/` folder from `pipeline/skills/shared/external/`.
98
+ The plugins' lifecycle skills - `create-component`, `evolve-component`,
99
+ `figma-utility`, `code-connect`, `branch-and-pr`, `fix-bug`, and the rest - are
100
+ hand-authored *inside* `multi-agent-plugins` and are never touched by sync.
101
+
102
+ ---
103
+
104
+ ## 2. Plugin marketplace build: one authoring source, five versioned artifacts
105
+
106
+ ```mermaid
107
+ graph TD
108
+ EXT["pipeline/skills/shared/external/<br/>150 skills - single authoring source<br/>(the pipeline's own phases read these too)"]
109
+
110
+ EXT -->|"cross-stack skills"| COMMONP["ai-common-toolkit<br/>10 skills · v0.2.3"]
111
+ EXT -->|"Apple/Xcode-only"| IOSP["ai-ios-toolkit<br/>145 skills · v0.6.0"]
112
+ EXT -->|"Android/Kotlin-only"| ANDP["ai-android-toolkit<br/>29 skills · v0.1.3"]
113
+ EXT -->|"backend-only"| BEP["ai-backend-toolkit<br/>32 skills · v0.1.4"]
114
+ EXT -->|"web/frontend-only"| FEP["ai-frontend-toolkit<br/>24 skills · v0.1.3"]
115
+
116
+ COMMONP --> BUMP{"skill set<br/>changed?"}
117
+ IOSP --> BUMP
118
+ ANDP --> BUMP
119
+ BEP --> BUMP
120
+ FEP --> BUMP
121
+ BUMP -->|yes| PATCH["bump that plugin's<br/>patch version"]
122
+ BUMP -->|no| SKIP["idempotent no-op"]
123
+
124
+ PATCH --> CONSUMER["/multi-agent:update<br/>→ claude marketplace update multi-agent-plugins"]
125
+
126
+ style EXT fill:#ffd,stroke:#333
127
+ ```
128
+
129
+ A skill counted in more than one platform plugin (a cross-stack knowledge skill
130
+ plus, say, an iOS-specific one) is why the plugins' skill counts sum to more than
131
+ the 150-skill source: `ai-common` skills are vendored into every stack plugin's
132
+ `knowledge/`, not deduplicated across them. Versioning is per-plugin and
133
+ patch-only from this generator - a repo enabling only `ai-ios-toolkit`
134
+ never pulls an Android-only change.
135
+
136
+ **Consumption is pull, not push.** A consumer repo enables a stack plugin once
137
+ (`/multi-agent:stack ios`, writing the enabled-plugins list into
138
+ `.claude/settings.json`) and picks up new plugin versions only when it runs
139
+ `/multi-agent:update`, which calls `claude marketplace update multi-agent-plugins`.
140
+ Publishing a new plugin version does not retroactively change anything already
141
+ running in a consumer's session.
142
+
143
+ ---
144
+
145
+ ## 3. Per-host delivery: the same three repos, three different shapes
146
+
147
+ The three repos land differently on each host, because each host's skill-loading
148
+ behavior is different (see `pipeline/multi-agent-refs/cross-cli-contract.md` for the
149
+ measurements behind this table):
150
+
151
+ | | Claude Code | Copilot CLI | Codex CLI |
152
+ |---|---|---|---|
153
+ | **Pipeline commands** | 50 slash-command skills, native | 50 skills, `multi-agent-{cmd}` naming, copied in | 1 router skill (`multi-agent`) + 50 command specs as reference files - Codex silently truncates its skills block past a few dozen entries, so sub-commands are not peer skills here |
154
+ | **Stack plugins** | Marketplace plugin, loaded natively, resolved by `.claude/settings.json` enabled-list | Enabled plugin's authored skills copied flat into `~/.copilot/skills/`; `knowledge/` **not** re-copied (already delivered via `shared/external`) | Copied as reference files under `~/.codex/multi-agent-refs/skills/`, plugin-prefixed on name clash (e.g. `architecture` → `ai-ios-toolkit-architecture`) |
155
+ | **Component dispatch (Phase 3)** | Marketplace plugin's `create-component`/`create-screen` skill via the Skill tool | No plugin loader - falls back to local frozen `figma-*` skill copies | Not part of the enforced parity axis; classification + state-shape must match, skill *inventory* does not |
156
+ | **dev-toolkit-mcp** | `claude mcp add dev-toolkit -- npx -y @mmerterden/dev-toolkit-mcp` | `copilot mcp add dev-toolkit -- npx -y @mmerterden/dev-toolkit-mcp` | `codex mcp add dev-toolkit -- npx -y @mmerterden/dev-toolkit-mcp` (skipped with a warning if `codex` isn't on `PATH`) |
157
+
158
+ `smoke-cross-cli-behavior.sh` and `smoke-codex-install.sh` gate the axes that **do**
159
+ have to match (phase labels, placeholder vocabulary, output schemas, command↔skill
160
+ parity count); the table above marks the axes that are allowed to diverge by design.
161
+
162
+ ---
163
+
164
+ ## 4. Runtime: what actually happens during a task
165
+
166
+ Two independent hand-offs happen inside a single pipeline run, neither aware of the
167
+ other:
168
+
169
+ ```mermaid
170
+ graph TD
171
+ START["Task running: Phase 3 (Dev)"]
172
+ START -->|"taskType !== component"| TDD["Standard TDD loop<br/>(pipeline's own code)"]
173
+ START -->|"taskType === component<br/>+ figmaUrl present"| VALIDATE["ai-ios-toolkit:figma-validate<br/>(registry, Code Connect, token compliance)"]
174
+ VALIDATE -->|pass| DISPATCH["Skill tool →<br/>create-component / create-screen<br/>/ evolve-component (dual-name fallback)"]
175
+ VALIDATE -->|fail| HALT1["halt Phase 3, surface why"]
176
+ DISPATCH --> REPORT1["plugin returns build/test status →<br/>dispatch layer writes state.phases['3'].subphases[]"]
177
+
178
+ REPORT1 --> P4["Phase 4: Review"]
179
+ P4 --> P5["Phase 5: Test"]
180
+
181
+ P5 -->|"UI bug hunt / manual-test /<br/>design-check / store-ready"| MCP["MCP tool call over stdio<br/>e.g. ios_xcodebuild, design_visual_compare,<br/>ios_app_store_audit"]
182
+ MCP --> DTKPROC["dev-toolkit-mcp process<br/>(npx @mmerterden/dev-toolkit-mcp)"]
183
+ DTKPROC -->|"result: screenshot / xcresult ID /<br/>18-rule audit verdict"| P5
184
+
185
+ style DISPATCH fill:#dfd,stroke:#333
186
+ style MCP fill:#dff,stroke:#333
187
+ style HALT1 fill:#fdd,stroke:#333
188
+ ```
189
+
190
+ **Phase 3 → plugin** is a one-shot delegation: the plugin skill does its own
191
+ lifecycle (test → code → build → wiki) and reports back a coarse
192
+ `component-build` result; the pipeline does not re-implement any of that logic, and
193
+ a plugin failure counts against the pipeline's own retry cap (`retryCount === 3` →
194
+ hard stop, per `component-dispatch.md`).
195
+
196
+ **Phase 5 (and design-check / store-ready) → dev-toolkit** is a long-lived MCP
197
+ session, not a one-shot call: the same stdio server process answers many tool
198
+ calls across a phase (boot simulator once, then screenshot/tap/screenshot/tap...).
199
+ Several pipeline skills pin a **minimum toolkit version** for a specific tool -
200
+ e.g. `apple-archive-compliance` requires `ios_app_store_audit` from
201
+ `dev-toolkit-mcp ≥ v2.9.0` - enforced in `cross-cli-contract.md` and checked by
202
+ `/multi-agent:sync` Step 3d before any dev-toolkit release ships (a version bump
203
+ that drops or renames a tool a pipeline skill depends on is a **major** bump, by
204
+ that step's own contract).
205
+
206
+ ### dev-toolkit-mcp's 80 tools, by category
207
+
208
+ | Category | Tools | Primary pipeline consumers |
209
+ |---|---|---|
210
+ | Device Control | 58 | `/multi-agent:test`, `test-dark-mode`, `test-accessibility`, `test-dynamic-type`, `test-screenshots`, `manual-test`, `design-check` |
211
+ | Accessibility Audit | 2 | `test-accessibility` |
212
+ | Store Compliance | 5 | `store-ready`, `testflight-validation`, `apple-archive-compliance` skill, Phase 4 Security Auditor |
213
+ | Web Automation | 8 | frontend-stack UI testing (via `test`) |
214
+ | Design Audit | 6 | `design-check` (mock-mode vs Figma conformance) |
215
+ | Autonomous Agent DSL | 1 | any skill that needs a scripted multi-step device flow in one round trip |
216
+
217
+ ---
218
+
219
+ ## 5. Why the boundary is drawn where it is
220
+
221
+ - **Pipeline ↔ plugins boundary = Skill tool, one direction.** The pipeline
222
+ classifies (`taskType`, `componentScope`) and tracks state; it never reads or
223
+ writes plugin-internal files. This is why a corporate marketplace can ship a
224
+ same-named plugin (`ai-ios-toolkit:create-ui-component` vs the public
225
+ `create-component`) and dispatch still resolves correctly - the dual-name
226
+ fallback lives in the pipeline, the implementation stays entirely in whichever
227
+ plugin is enabled.
228
+ - **Pipeline ↔ dev-toolkit boundary = MCP protocol, versioned contract.** The
229
+ pipeline never shells out to `xcrun simctl` or `adb` directly; every device/browser
230
+ action is a declared MCP tool call with a minimum-version pin. That's what lets
231
+ `dev-toolkit-mcp` ship on its own release cadence (its own gates, its own
232
+ `npm publish`) without a pipeline release, as long as pinned tools keep their
233
+ contract.
234
+ - **Neither boundary is symmetric.** The pipeline depends on both other repos being
235
+ present *for specific task types* (component work, UI testing) but functions
236
+ without either - a non-component bugfix task never touches the plugin marketplace,
237
+ and a task with no UI-testing step never opens the MCP connection.
package/docs/features.md CHANGED
@@ -45,14 +45,14 @@ Build commands, test runners, lint tools, and review focus areas all adapt to th
45
45
 
46
46
  ### Stack Selection (marketplace plugins)
47
47
 
48
- Stack skill sets ship as versioned plugins in the `multi-agent-plugins` marketplace. Selecting a stack enables the matching plugin(s) in the current repo's `.claude/settings.json` `enabledPlugins` - no skill copying, no session restart tricks, no directory shuffling. The `ai-common-engineering-toolkit` (accessibility audit, humanizer, Firebase) is always enabled alongside the stack plugin.
48
+ Stack skill sets ship as versioned plugins in the `multi-agent-plugins` marketplace. Selecting a stack enables the matching plugin(s) in the current repo's `.claude/settings.json` `enabledPlugins` - no skill copying, no session restart tricks, no directory shuffling. The `ai-common-toolkit` (accessibility audit, humanizer, Firebase) is always enabled alongside the stack plugin.
49
49
 
50
50
  ```bash
51
- /multi-agent:stack ios # ai-ios-engineering-toolkit (SwiftUI, Xcode, HIG)
52
- /multi-agent:stack android # ai-android-engineering-toolkit (Compose, Gradle, Hilt)
51
+ /multi-agent:stack ios # ai-ios-toolkit (SwiftUI, Xcode, HIG)
52
+ /multi-agent:stack android # ai-android-toolkit (Compose, Gradle, Hilt)
53
53
  /multi-agent:stack mobile # iOS + Android combined
54
54
  /multi-agent:stack backend # ai-backend-toolkit (spec-driven APIs)
55
- /multi-agent:stack frontend # ai-frontend-engineering-toolkit (React/TSX)
55
+ /multi-agent:stack frontend # ai-frontend-toolkit (React/TSX)
56
56
  /multi-agent:stack fullstack # backend + frontend
57
57
  /multi-agent:stack all # every stack plugin
58
58
  ```
@@ -273,7 +273,7 @@ Turn a recurring, project-specific job into a first-class `/multi-agent:<name>`
273
273
 
274
274
  ### Figma / Component Generation (dispatched to marketplace plugins)
275
275
 
276
- Component + Figma-to-code work is no longer bundled in this repo. When Phase 0 classifies a task as `component`, Phase 3 dispatches it to the per-stack marketplace plugins (`ai-ios-engineering-toolkit` / `ai-android-engineering-toolkit` in the `multi-agent-plugins` marketplace) via the Skill tool. The plugin's component skill generates `{Name}Configuration.swift`, `{Name}View.swift`, `{Name}+Modifiers.swift`, `{Name}.figma.swift`, and `FIGMA.md` with a variant matrix, then runs a 14-item pre-commit checklist covering design tokens, accessibility, tests, and Code Connect.
276
+ Component + Figma-to-code work is no longer bundled in this repo. When Phase 0 classifies a task as `component`, Phase 3 dispatches it to the per-stack marketplace plugins (`ai-ios-toolkit` / `ai-android-toolkit` in the `multi-agent-plugins` marketplace) via the Skill tool. The plugin's component skill generates `{Name}Configuration.swift`, `{Name}View.swift`, `{Name}+Modifiers.swift`, `{Name}.figma.swift`, and `FIGMA.md` with a variant matrix, then runs a 14-item pre-commit checklist covering design tokens, accessibility, tests, and Code Connect.
277
277
 
278
278
  The plugin's cross-cutting integration skills feed component detection + implementation when the design triggers them (content: form / price / ui-patterns; interaction: navigation / overlays / bottom-sheets). Each is native-SwiftUI-first and reads project specifics (token namespaces, component paths, UI systems) from `figma-config`, including the optional `ui.navigationSystem` / `ui.overlaySystem` / `ui.sheetSystem` hooks (absent -> stock SwiftUI), so the same capabilities work on any SwiftUI codebase. The plugin's evolve-component skill reconciles an existing component against current Figma (drift-heal) and additively extends it, behind a human gate.
279
279
 
package/index.js CHANGED
@@ -59,7 +59,9 @@ if (command === "--version" || command === "-v" || command === "version") {
59
59
  npx @mmerterden/multi-agent-pipeline uninstall --yes Skip prompt
60
60
  npx @mmerterden/multi-agent-pipeline uninstall --dry-run Report what would be removed
61
61
  npx @mmerterden/multi-agent-pipeline uninstall --claude Only Claude Code
62
+ npx @mmerterden/multi-agent-pipeline uninstall --copilot Only Copilot CLI
62
63
  npx @mmerterden/multi-agent-pipeline uninstall --codex Only Codex CLI
64
+ npx @mmerterden/multi-agent-pipeline uninstall --all-data ALSO remove pipeline settings + logs/state/metrics
63
65
  npx @mmerterden/multi-agent-pipeline uninstall --cursor Legacy pre-v10.7 adapter-file cleanup (also --copilot-chat / --antigravity; --target=<path> overrides cwd)
64
66
 
65
67
  Help:
@@ -24,8 +24,17 @@ import { ensureDir, ensureRealDir, isDryRun, writeFile } from "./_common.mjs";
24
24
  * The pipeline's ladder is `fable -> opus -> sonnet -> haiku`. Codex offers no
25
25
  * Anthropic models, so each tier maps onto an OpenAI model plus an effort
26
26
  * setting: effort carries the depth distinction that the model id carries on
27
- * Claude Code. Keep this table in sync with the Codex column of the Phase 4
28
- * reviewer matrix and with `pipeline/scripts/cost-table.json`.
27
+ * Claude Code.
28
+ *
29
+ * This table resolves a persona's DEFAULT tier, which is not the same thing as
30
+ * a per-slot override. Phase 4 dispatches Reviewer 3 with an explicit
31
+ * `gpt-5.6` @ `medium` (see `phases/phase-4-review.md`, `claude-md-template.md`
32
+ * and `reviewer-output.schema.json`, which all state that value) even though
33
+ * the persona's own tier is `sonnet` and resolves here to `gpt-5.4`. That is
34
+ * deliberate: the Codex panel buys its diversity from effort, so two slots
35
+ * share a model at different efforts while a third changes model. Do not
36
+ * "reconcile" the two by editing either side without deciding which behavior
37
+ * you want, and keep this table in sync with `pipeline/scripts/cost-table.json`.
29
38
  */
30
39
  export const CODEX_TIER_MAP = Object.freeze({
31
40
  fable: { model: "gpt-5.6", reasoning_effort: "xhigh" },
@@ -10,11 +10,14 @@
10
10
  */
11
11
 
12
12
  import {
13
+ chmodSync,
13
14
  cpSync,
14
15
  existsSync,
15
16
  lstatSync,
16
17
  mkdirSync,
17
18
  readdirSync,
19
+ realpathSync,
20
+ renameSync,
18
21
  rmSync,
19
22
  statSync,
20
23
  symlinkSync,
@@ -279,6 +282,24 @@ export const ABANDONED_TREES = Object.freeze([
279
282
  },
280
283
  ]);
281
284
 
285
+ /**
286
+ * Registry of command renames. Command names are an interface: users type
287
+ * them, docs and saved routines reference them, and Copilot installs derive
288
+ * `multi-agent-<name>` skill dirs from them. A shipped name may only ever
289
+ * disappear by being recorded here as `oldName: newName` - the
290
+ * install-lifecycle test compares the shipped tree against its committed
291
+ * snapshot and fails on any removal that has no rename entry, so a command
292
+ * cannot vanish between versions by accident.
293
+ *
294
+ * @type {Readonly<Record<string, string>>}
295
+ */
296
+ export const COMMAND_RENAMES = Object.freeze({
297
+ delete: "uninstall",
298
+ // v15.0.0: "continue local work through the pipeline tail" reads as a resume
299
+ // variant, not a shipping action (the command opens the PR but does not merge).
300
+ ship: "resume-local",
301
+ });
302
+
282
303
  /**
283
304
  * Remove trees an older install left behind.
284
305
  *
@@ -404,7 +425,50 @@ export function writeFile(path, content) {
404
425
  console.log(` [dry-run] would write ${path}`);
405
426
  return;
406
427
  }
407
- writeFileSync(path, content);
428
+ atomicWrite(path, content);
429
+ }
430
+
431
+ /**
432
+ * tmp + rename so a crash mid-write can never leave a truncated file behind -
433
+ * several call sites rewrite host config the CLI needs in order to start
434
+ * (`~/.claude/settings.json`, `copilot-instructions.md`).
435
+ *
436
+ * Two properties a naive rename would silently destroy on co-owned files:
437
+ *
438
+ * - **symlinks**: users keep these files in a dotfiles repo and symlink them
439
+ * in. Renaming over the link replaces it with a regular file, so the
440
+ * dotfiles copy goes stale and the next dotfiles apply reverts our edits.
441
+ * Resolve the link first and write through it.
442
+ * - **mode**: a rename installs the tmp file's default 0644 over whatever the
443
+ * user set (0600 on a settings file holding tokens is a real case). Carry
444
+ * the existing mode across.
445
+ *
446
+ * @param {string} path
447
+ * @param {string} content
448
+ */
449
+ export function atomicWrite(path, content) {
450
+ let target = path;
451
+ try {
452
+ if (lstatSync(path).isSymbolicLink()) target = realpathSync(path);
453
+ } catch {
454
+ // Missing file: nothing to preserve, write the path as given.
455
+ }
456
+ let mode;
457
+ try {
458
+ mode = statSync(target).mode & 0o777;
459
+ } catch {
460
+ mode = undefined;
461
+ }
462
+ const tmp = `${target}.tmp-${process.pid}`;
463
+ writeFileSync(tmp, content);
464
+ if (mode !== undefined) {
465
+ try {
466
+ chmodSync(tmp, mode);
467
+ } catch {
468
+ // Best effort: a failed chmod must not lose the write.
469
+ }
470
+ }
471
+ renameSync(tmp, target);
408
472
  }
409
473
 
410
474
  /**
@@ -64,7 +64,6 @@ const DEV_ONLY_TOOLING = Object.freeze([
64
64
  "validate-schemas.mjs", // validates the repo's own schema files, needs ajv
65
65
  "sync-parity-check.sh",
66
66
  "benchmark-phase-0.sh",
67
- "test-gap-rules", // rule corpus for the repo's own test-gap gate
68
67
  ]);
69
68
 
70
69
  /** Eval harnesses (`eval-*.mjs`) read `pipeline/eval/**`, which never ships. */
@@ -7,9 +7,13 @@
7
7
  * @module install/_platform-filter
8
8
  */
9
9
 
10
- import { readdirSync } from "fs";
10
+ import { readdirSync, writeFileSync } from "fs";
11
11
  import { join } from "path";
12
- import { copyDir, countFiles, ensureDir } from "./_common.mjs";
12
+ import { copyDir, countFiles, ensureDir, isDryRun } from "./_common.mjs";
13
+ import { routeSkill } from "../pipeline/scripts/_stack-routing.mjs";
14
+
15
+ /** Uninstall reads this to know exactly which external skill dirs are ours to remove. */
16
+ export const EXTERNAL_SKILLS_MANIFEST = ".external-skills-manifest.json";
13
17
 
14
18
  /**
15
19
  * Heuristic prefix lists - intentionally loose so new skills added to
@@ -107,26 +111,88 @@ export function classifyExternalSkill(skillName) {
107
111
  */
108
112
  export function copyExternalSkillsFiltered(externalSrc, dest, opts) {
109
113
  const { platformFlag, useSymlinks = false } = opts;
114
+ const allNames = readdirSync(externalSrc, { withFileTypes: true })
115
+ .filter((e) => e.isDirectory())
116
+ .map((e) => e.name);
117
+
110
118
  if (platformFlag === "all") {
111
119
  copyDir(externalSrc, dest, { useSymlinks });
120
+ writeExternalSkillsManifest(dest, allNames);
112
121
  return { copied: countFiles(externalSrc), skipped: 0 };
113
122
  }
114
123
 
115
124
  ensureDir(dest);
116
125
  let copied = 0;
117
126
  let skipped = 0;
118
- for (const entry of readdirSync(externalSrc, { withFileTypes: true })) {
119
- if (!entry.isDirectory()) continue;
120
- const classification = classifyExternalSkill(entry.name);
127
+ const delivered = [];
128
+ for (const name of allNames) {
129
+ const classification = classifyExternalSkill(name);
121
130
  const shouldCopy = classification === "generic" || classification === platformFlag;
122
- const src = join(externalSrc, entry.name);
123
- const dst = join(dest, entry.name);
131
+ const src = join(externalSrc, name);
132
+ const dst = join(dest, name);
124
133
  if (shouldCopy) {
125
134
  copyDir(src, dst, { useSymlinks });
126
135
  copied += countFiles(src);
136
+ delivered.push(name);
127
137
  } else {
128
138
  skipped += 1;
129
139
  }
130
140
  }
141
+ writeExternalSkillsManifest(dest, delivered);
131
142
  return { copied, skipped };
132
143
  }
144
+
145
+ /**
146
+ * Enabled-stack partition of the external skill catalog.
147
+ *
148
+ * The prefix classifier above only knows ios/android/generic, so it cannot
149
+ * express "backend toolkit not enabled". Routing can: `routeSkill` is the same
150
+ * table `build-stack-plugins.mjs` ships plugins with, so filtering by it keeps
151
+ * a host's local copy byte-aligned with what the enabled plugins would serve on
152
+ * Claude Code. Unrouted skills are kept - dropping a skill no table claims
153
+ * would make it unreachable everywhere.
154
+ *
155
+ * @param {string} externalSrc - absolute path to `pipeline/skills/shared/external/`
156
+ * @param {string[]} enabledPluginNames - e.g. ["ai-ios-toolkit", "ai-common-toolkit"]
157
+ * @returns {{ keep: string[], skipped: string[] }}
158
+ */
159
+ export function partitionExternalSkillsByPlugins(externalSrc, enabledPluginNames) {
160
+ const keep = [];
161
+ const skipped = [];
162
+ for (const e of readdirSync(externalSrc, { withFileTypes: true })) {
163
+ if (!e.isDirectory()) continue;
164
+ const plugins = routeSkill(e.name);
165
+ if (plugins.length === 0 || plugins.some((p) => enabledPluginNames.includes(p))) {
166
+ keep.push(e.name);
167
+ } else {
168
+ skipped.push(e.name);
169
+ }
170
+ }
171
+ return { keep, skipped };
172
+ }
173
+
174
+ /**
175
+ * Record exactly which external skill dirs THIS install delivered.
176
+ *
177
+ * Uninstall needs to remove the distributed catalog without touching
178
+ * user-authored skill dirs that happen to share a name. The shipped
179
+ * `.skills-index.json` cannot answer that: it is the full catalog, so it lists
180
+ * platform-filtered skills that were never installed, and a newer package's
181
+ * tree lists skills the installed version never shipped. Both cases would make
182
+ * uninstall delete a user's own directory. Same contract as
183
+ * `.plugin-skills-manifest.json` for plugin-delivered skills.
184
+ *
185
+ * @param {string} dest
186
+ * @param {string[]} names
187
+ */
188
+ export function writeExternalSkillsManifest(dest, names) {
189
+ if (isDryRun()) return;
190
+ try {
191
+ writeFileSync(
192
+ join(dest, EXTERNAL_SKILLS_MANIFEST),
193
+ JSON.stringify([...names].sort(), null, 2) + "\n",
194
+ );
195
+ } catch {
196
+ /* best-effort - a missing manifest just means uninstall falls back */
197
+ }
198
+ }
@@ -24,9 +24,12 @@
24
24
  * @module install/_plugin-skills
25
25
  */
26
26
 
27
- import { existsSync, readFileSync, readdirSync, statSync } from "fs";
27
+ import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
28
28
  import { join } from "path";
29
29
 
30
+ /** Uninstall reads this to know exactly which delivered skill dirs are ours to remove. */
31
+ export const PLUGIN_SKILLS_MANIFEST = ".plugin-skills-manifest.json";
32
+
30
33
  import { copyDir, countFiles, ensureDir, ensureRealDir, isDryRun, wipeDir } from "./_common.mjs";
31
34
 
32
35
  /** Subtrees a plugin authors itself. `knowledge/` is generated, so it is excluded. */
@@ -34,11 +37,11 @@ export const AUTHORED_GROUPS = Object.freeze(["index", "reference", "workflow",
34
37
 
35
38
  /** Stack plugins, and the platform each belongs to. */
36
39
  export const STACK_PLUGINS = Object.freeze([
37
- { name: "ai-ios-engineering-toolkit", platform: "ios" },
38
- { name: "ai-android-engineering-toolkit", platform: "android" },
39
- { name: "ai-frontend-engineering-toolkit", platform: "all" },
40
+ { name: "ai-ios-toolkit", platform: "ios" },
41
+ { name: "ai-android-toolkit", platform: "android" },
42
+ { name: "ai-frontend-toolkit", platform: "all" },
40
43
  { name: "ai-backend-toolkit", platform: "all" },
41
- { name: "ai-common-engineering-toolkit", platform: "all" },
44
+ { name: "ai-common-toolkit", platform: "all" },
42
45
  ]);
43
46
 
44
47
  /**
@@ -116,7 +119,7 @@ export function pluginsToDeliver(home, platformFlag) {
116
119
  const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
117
120
  const enabled = Object.entries(settings.enabledPlugins || {})
118
121
  .filter(([, on]) => on === true)
119
- // keys look like `ai-ios-engineering-toolkit@multi-agent-plugins`
122
+ // keys look like `ai-ios-toolkit@multi-agent-plugins`
120
123
  .map(([k]) => k.split("@")[0])
121
124
  .filter((n) => STACK_PLUGINS.some((p) => p.name === n));
122
125
  if (enabled.length > 0) {
@@ -191,7 +194,7 @@ export function installAuthoredPluginSkills(opts) {
191
194
  // That is not a duplicate: the pipeline's `architecture` is a generic ADR
192
195
  // framework while the iOS plugin's is that stack's structural rules, and the
193
196
  // same holds for `backlog`. Claude Code reaches both because its loader
194
- // namespaces plugin skills (`ai-ios-engineering-toolkit:architecture`); the
197
+ // namespaces plugin skills (`ai-ios-toolkit:architecture`); the
195
198
  // copy hosts had no namespace, so the stack-specific version was silently
196
199
  // unreachable on exactly the repos that need it most.
197
200
  //
@@ -254,7 +257,29 @@ export function installAuthoredPluginSkills(opts) {
254
257
  `clone {owner}/multi-agent-plugins or enable them in Claude Code to deliver their skills`,
255
258
  );
256
259
  }
257
- return { copied, collided, renamed, plugins, missing, selectionSource };
260
+ // Uninstall has no way to know which of ~100 flat skill dirs came from a
261
+ // plugin delivery pass rather than the pipeline's own PIPELINE_CORE_SKILL_DIRS
262
+ // allowlist - it silently left all of them behind. Persist exactly the names
263
+ // this pass delivered so uninstall can remove precisely those, nothing more.
264
+ if (!isDryRun()) {
265
+ try {
266
+ writeFileSync(
267
+ join(dest, PLUGIN_SKILLS_MANIFEST),
268
+ JSON.stringify([...delivered], null, 2) + "\n",
269
+ );
270
+ } catch {
271
+ /* best-effort - a missing manifest just means uninstall skips this cleanup */
272
+ }
273
+ }
274
+ return {
275
+ copied,
276
+ collided,
277
+ renamed,
278
+ plugins,
279
+ missing,
280
+ selectionSource,
281
+ deliveredNames: [...delivered],
282
+ };
258
283
  }
259
284
 
260
285
  /**