@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.
- package/CHANGELOG.md +118 -6
- package/README.md +20 -9
- package/README.tr.md +150 -0
- package/docs/FIGMA_PIPELINE.md +3 -3
- package/docs/adr/0006-skills-core-external-split.md +1 -1
- package/docs/adr/0009-claude-stack-skills-plugin-only.md +31 -0
- package/docs/adr/README.md +1 -0
- package/docs/architecture.md +24 -9
- package/docs/ecosystem.md +237 -0
- package/docs/features.md +5 -5
- package/index.js +2 -0
- package/install/_codex-agents.mjs +11 -2
- package/install/_common.mjs +65 -1
- package/install/_dev-only-files.mjs +0 -1
- package/install/_platform-filter.mjs +73 -7
- package/install/_plugin-skills.mjs +33 -8
- package/install/claude.mjs +144 -59
- package/install/codex.mjs +37 -7
- package/install/copilot.mjs +36 -11
- package/install/index.mjs +6 -2
- package/install/templates/codex-instructions.md +1 -1
- package/install/templates/copilot-instructions.md +15 -12
- package/package.json +1 -2
- package/pipeline/commands/multi-agent/SKILL.md +4 -2
- package/pipeline/commands/multi-agent/analysis/SKILL.md +5 -5
- package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/autopilot/SKILL.md +6 -2
- package/pipeline/commands/multi-agent/build-optimize/SKILL.md +9 -9
- package/pipeline/commands/multi-agent/channels/SKILL.md +16 -5
- package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +186 -0
- package/pipeline/commands/multi-agent/create-jira/SKILL.md +4 -4
- package/pipeline/commands/multi-agent/dev/SKILL.md +10 -23
- package/pipeline/commands/multi-agent/dev-autopilot/SKILL.md +10 -2
- package/pipeline/commands/multi-agent/dev-local/SKILL.md +10 -24
- package/pipeline/commands/multi-agent/dev-local-autopilot/SKILL.md +10 -3
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/help/SKILL.md +19 -4
- package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/jira/SKILL.md +13 -2
- package/pipeline/commands/multi-agent/language/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/local/SKILL.md +6 -2
- package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +6 -2
- package/pipeline/commands/multi-agent/log/SKILL.md +7 -1
- package/pipeline/commands/multi-agent/prune-prompts/SKILL.md +81 -0
- package/pipeline/commands/multi-agent/resume/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/{ship → resume-local}/SKILL.md +13 -9
- package/pipeline/commands/multi-agent/setup/SKILL.md +5 -5
- package/pipeline/commands/multi-agent/stack/SKILL.md +55 -43
- package/pipeline/commands/multi-agent/store-ready/SKILL.md +3 -3
- package/pipeline/commands/multi-agent/sync/SKILL.md +18 -11
- package/pipeline/commands/multi-agent/testflight-validation/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/uninstall/SKILL.md +2 -0
- package/pipeline/commands/multi-agent/update/SKILL.md +1 -1
- package/pipeline/lib/extract-conventions.sh +44 -15
- package/pipeline/lib/fetch-figma-annotations.sh +8 -1
- package/pipeline/lib/fetch-fortify.sh +23 -8
- package/pipeline/lib/figma-screenshot.sh +11 -1
- package/pipeline/lib/issue-fetcher.sh +77 -10
- package/pipeline/lib/md2confluence-v3.py +16 -2
- package/pipeline/lib/parse-complaints.sh +306 -0
- package/pipeline/lib/plan-todos.sh +5 -2
- package/pipeline/lib/post-pr-review.sh +8 -6
- package/pipeline/lib/shadow-git.sh +50 -9
- package/pipeline/lib/submodule-detector.sh +8 -1
- package/pipeline/multi-agent-refs/_input-parser.md +1 -1
- package/pipeline/multi-agent-refs/channels/confluence.md +3 -0
- package/pipeline/multi-agent-refs/channels/issue-comment.md +2 -2
- package/pipeline/multi-agent-refs/channels/jira.md +13 -2
- package/pipeline/multi-agent-refs/channels/pr-review-actions.md +1 -1
- package/pipeline/multi-agent-refs/channels/pr.md +20 -0
- package/pipeline/multi-agent-refs/channels/wiki.md +4 -4
- package/pipeline/multi-agent-refs/complaint-analysis-template.md +99 -0
- package/pipeline/multi-agent-refs/component-dispatch.md +6 -6
- package/pipeline/multi-agent-refs/cross-cli-contract.md +16 -16
- package/pipeline/multi-agent-refs/features/external-context-injection.md +1 -1
- package/pipeline/multi-agent-refs/features/stack-skill-routing.md +5 -5
- package/pipeline/multi-agent-refs/features/worktree-finalize.md +1 -1
- package/pipeline/multi-agent-refs/generate-issue.md +3 -3
- package/pipeline/multi-agent-refs/issue-jira-triad.md +3 -3
- package/pipeline/multi-agent-refs/payload-contracts.md +67 -0
- package/pipeline/multi-agent-refs/phases/modes.md +20 -0
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +2 -2
- package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +7 -7
- package/pipeline/multi-agent-refs/phases/phase-2-planning.md +5 -5
- package/pipeline/multi-agent-refs/phases/phase-3-dev.md +3 -3
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +12 -12
- package/pipeline/multi-agent-refs/phases/phase-5-test.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-6-commit.md +8 -40
- package/pipeline/multi-agent-refs/phases/phase-7-report.md +5 -3
- package/pipeline/multi-agent-refs/phases.md +6 -0
- package/pipeline/multi-agent-refs/rules.md +2 -0
- package/pipeline/multi-agent-refs/tracker-contract.md +1 -1
- package/pipeline/multi-agent-refs/wiki-capture.md +2 -2
- package/pipeline/preferences-template.json +13 -5
- package/pipeline/rules/figma-pipeline.md +2 -2
- package/pipeline/schemas/agent-state.schema.json +1 -1
- package/pipeline/schemas/complaint-analysis-spec.schema.json +216 -0
- package/pipeline/schemas/migrations/prefs-2.5.0-to-2.6.0.mjs +46 -0
- package/pipeline/schemas/prefs.schema.json +277 -67
- package/pipeline/schemas/token-budget.json +2 -2
- package/pipeline/scripts/_stack-routing.mjs +79 -0
- package/pipeline/scripts/audit-log-rotate.sh +14 -1
- package/pipeline/scripts/build-skills-index.mjs +11 -0
- package/pipeline/scripts/build-stack-plugins.mjs +35 -60
- package/pipeline/scripts/check-derived-drift.mjs +65 -29
- package/pipeline/scripts/diff-explain.mjs +41 -3
- package/pipeline/scripts/diff-risk-score.mjs +72 -8
- package/pipeline/scripts/gc-worktrees.sh +4 -1
- package/pipeline/scripts/gen-mode-dispatch.mjs +1 -1
- package/pipeline/scripts/gen-skills-index.mjs +1 -1
- package/pipeline/scripts/learning-curve.mjs +8 -2
- package/pipeline/scripts/match-skills.mjs +8 -2
- package/pipeline/scripts/migrate-prefs.mjs +28 -20
- package/pipeline/scripts/output-quality-check.sh +15 -4
- package/pipeline/scripts/phase-tracker.sh +33 -12
- package/pipeline/scripts/phase0-exit-gate.mjs +3 -2
- package/pipeline/scripts/pre-commit-check.sh +69 -22
- package/pipeline/scripts/render-agent-log-cost.sh +8 -3
- package/pipeline/scripts/render-cost-summary.sh +42 -22
- package/pipeline/scripts/render-work-summary.sh +47 -13
- package/pipeline/scripts/review-scope.mjs +1 -1
- package/pipeline/scripts/run-aggregator.mjs +43 -14
- package/pipeline/scripts/scan-agent-config.sh +1 -1
- package/pipeline/scripts/skill-conformance.mjs +165 -30
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +1 -1
- package/pipeline/scripts/smoke-schema-validation.sh +5 -1
- package/pipeline/scripts/test-gap-rules/android.json +25 -0
- package/pipeline/scripts/test-gap-rules/ios.json +34 -0
- package/pipeline/scripts/test-gap-rules/node.json +29 -0
- package/pipeline/scripts/test-gap-rules/python.json +25 -0
- package/pipeline/scripts/test-gap-scan.mjs +45 -6
- package/pipeline/scripts/uninstall.mjs +196 -14
- package/pipeline/scripts/update-issue-progress.sh +12 -16
- package/pipeline/scripts/validate-complaint-doc.mjs +229 -0
- package/pipeline/scripts/validate-reviewer.mjs +9 -3
- package/pipeline/scripts/worktree-finalize.sh +23 -2
- package/pipeline/skills/.skill-manifest.json +156 -108
- package/pipeline/skills/.skills-index.json +459 -13
- package/pipeline/skills/shared/README.md +15 -11
- package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +4 -0
- package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-complaint-analysis/SKILL.md +49 -0
- package/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev/SKILL.md +4 -17
- package/pipeline/skills/shared/core/multi-agent-dev-autopilot/SKILL.md +8 -0
- package/pipeline/skills/shared/core/multi-agent-dev-local/SKILL.md +5 -18
- package/pipeline/skills/shared/core/multi-agent-dev-local-autopilot/SKILL.md +8 -0
- package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +4 -0
- package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +4 -0
- package/pipeline/skills/shared/core/multi-agent-prune-prompts/SKILL.md +83 -0
- package/pipeline/skills/shared/core/{multi-agent-ship → multi-agent-resume-local}/SKILL.md +10 -6
- package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +79 -22
- package/pipeline/skills/shared/core/multi-agent-store-ready/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +8 -8
- package/pipeline/skills/shared/core/multi-agent-testflight-validation/SKILL.md +1 -1
- package/pipeline/skills/shared/external/ios-coding-standard/modules/_TEMPLATE.yml +2 -2
- package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +368 -33
- package/pipeline/skills/shared/external/ios-coding-standard/references/swiftlint.draft.yml +1 -2
- package/pipeline/skills/shared/external/ios-coding-standard/scripts/check_structure.py +765 -0
- package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +75 -0
- package/pipeline/skills/shared/external/ios-module-structure/modules/_TEMPLATE.yml +131 -0
- package/pipeline/skills/shared/external/ios-module-structure/references/rules.yml +559 -0
- package/pipeline/skills/shared/external/ios-module-structure/scripts/check_structure.py +765 -0
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +302 -0
- package/pipeline/skills/shared/external/localization-reuse-map/example-mapping.json +187 -0
- package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +156 -0
- package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +108 -0
- package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +176 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +865 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +335 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +344 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +130 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +264 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +298 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +529 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +187 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +171 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +184 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +26 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +173 -0
- 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-
|
|
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-
|
|
52
|
-
/multi-agent:stack android # ai-android-
|
|
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-
|
|
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-
|
|
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.
|
|
28
|
-
*
|
|
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" },
|
package/install/_common.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
const classification = classifyExternalSkill(
|
|
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,
|
|
123
|
-
const dst = join(dest,
|
|
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-
|
|
38
|
-
{ name: "ai-android-
|
|
39
|
-
{ name: "ai-frontend-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
/**
|