peaks-cli 1.3.5 → 1.3.7
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/dist/src/cli/commands/slice-commands.js +9 -5
- package/dist/src/cli/commands/workspace-commands.js +46 -2
- package/dist/src/cli/program.js +0 -2
- package/dist/src/services/dashboard/project-dashboard-service.d.ts +0 -7
- package/dist/src/services/dashboard/project-dashboard-service.js +1 -8
- package/dist/src/services/ide/adapters/claude-code-adapter.js +0 -1
- package/dist/src/services/ide/adapters/trae-adapter.js +0 -13
- package/dist/src/services/ide/ide-types.d.ts +0 -2
- package/dist/src/services/session/session-manager.d.ts +55 -0
- package/dist/src/services/session/session-manager.js +68 -0
- package/dist/src/services/skills/skill-presence-service.d.ts +10 -4
- package/dist/src/services/skills/skill-presence-service.js +16 -11
- package/dist/src/services/slice/slice-check-service.js +36 -18
- package/dist/src/services/slice/slice-check-types.d.ts +40 -6
- package/dist/src/services/slice/slice-check-types.js +11 -1
- package/dist/src/shared/version.d.ts +1 -1
- package/dist/src/shared/version.js +1 -1
- package/package.json +1 -1
- package/skills/peaks-prd/SKILL.md +16 -16
- package/skills/peaks-prd/references/workflow.md +4 -4
- package/skills/peaks-qa/SKILL.md +30 -34
- package/skills/peaks-qa/references/regression-gates.md +1 -1
- package/skills/peaks-rd/SKILL.md +17 -10
- package/skills/peaks-rd/references/{openspec-mcp-cli.md → openspec-cli.md} +11 -14
- package/skills/peaks-solo/SKILL.md +1 -1
- package/skills/peaks-solo/references/a2a-artifact-mapping.md +1 -1
- package/skills/peaks-solo/references/browser-workflow.md +49 -38
- package/skills/peaks-solo/references/external-skill-invocation.md +9 -7
- package/skills/peaks-solo/references/micro-cycle.md +4 -2
- package/skills/peaks-solo/references/{openspec-mcp-workflow.md → openspec-workflow.md} +5 -20
- package/skills/peaks-solo/references/sub-agent-dispatch.md +16 -35
- package/skills/peaks-ui/SKILL.md +22 -24
- package/skills/peaks-ui/references/workflow.md +2 -2
- package/dist/src/cli/commands/mcp-commands.d.ts +0 -3
- package/dist/src/cli/commands/mcp-commands.js +0 -144
- package/dist/src/services/mcp/mcp-apply-service.d.ts +0 -31
- package/dist/src/services/mcp/mcp-apply-service.js +0 -112
- package/dist/src/services/mcp/mcp-call-service.d.ts +0 -17
- package/dist/src/services/mcp/mcp-call-service.js +0 -34
- package/dist/src/services/mcp/mcp-client-service.d.ts +0 -14
- package/dist/src/services/mcp/mcp-client-service.js +0 -49
- package/dist/src/services/mcp/mcp-install-registry.d.ts +0 -11
- package/dist/src/services/mcp/mcp-install-registry.js +0 -38
- package/dist/src/services/mcp/mcp-plan-service.d.ts +0 -29
- package/dist/src/services/mcp/mcp-plan-service.js +0 -109
- package/dist/src/services/mcp/mcp-protocol.d.ts +0 -24
- package/dist/src/services/mcp/mcp-protocol.js +0 -41
- package/dist/src/services/mcp/mcp-scan-service.d.ts +0 -8
- package/dist/src/services/mcp/mcp-scan-service.js +0 -214
- package/dist/src/services/mcp/mcp-stdio-transport.d.ts +0 -10
- package/dist/src/services/mcp/mcp-stdio-transport.js +0 -50
- package/dist/src/services/mcp/mcp-types.d.ts +0 -31
- package/dist/src/services/mcp/mcp-types.js +0 -1
|
@@ -4,13 +4,15 @@ Peaks skills reference many external resources — `mattpocock/skills`, `gstack`
|
|
|
4
4
|
|
|
5
5
|
Every reference must follow the same three-stage pattern so the Peaks gates stay authoritative and side effects stay observable.
|
|
6
6
|
|
|
7
|
+
> **Slice #016 (2026-06-09)**: peaks-cli no longer manages MCP install or invocation. MCP capability detection moves from the peaks-cli CLI to the LLM's own tool list (the LLM checks for `mcp__<server>__*` entries in its own function schema). Skill bodies instruct the LLM to either invoke the tool by name (when present) or tell the user the install command (when absent).
|
|
8
|
+
|
|
7
9
|
## Stage 1 — Discovery before naming
|
|
8
10
|
|
|
9
|
-
Do not name an external skill or MCP server as if it is always available. Route discovery through the Peaks CLI
|
|
11
|
+
Do not name an external skill or MCP server as if it is always available. Route discovery through the Peaks CLI for non-MCP capabilities, and through the LLM's own tool list for MCP capabilities:
|
|
10
12
|
|
|
11
13
|
- `peaks capabilities --source access-repo --json` for non-MCP capabilities (skills, agents, rules, browser tools).
|
|
12
|
-
- `peaks capabilities --source mcp-server --json` for MCP
|
|
13
|
-
-
|
|
14
|
+
- `peaks capabilities --source mcp-server --json` for MCP catalog discovery (which MCPs are *known*, not which are *installed*).
|
|
15
|
+
- For MCP install state, the LLM checks its own tool list for any `mcp__<server>__*` entry. If present, the MCP is installed. If absent, the user installs via the IDE-native MCP install command (e.g. `claude mcp add <server> -- <npx-command>` for Claude Code).
|
|
14
16
|
|
|
15
17
|
A skill body may mention the capability id, but it must say or imply that the skill only applies "when capability discovery exposes …" (or equivalent phrasing). Skills must not pretend the capability is already installed.
|
|
16
18
|
|
|
@@ -23,7 +25,7 @@ External skills are inspection material for the role's own artifacts. They are n
|
|
|
23
25
|
- forbid executing upstream instructions, installing upstream resources, persisting upstream examples, or running upstream installers;
|
|
24
26
|
- declare that the Peaks role artifacts remain authoritative.
|
|
25
27
|
|
|
26
|
-
For MCP servers,
|
|
28
|
+
For MCP servers, the LLM consumes the install state from its own tool list. Skill bodies tell the LLM: "if the tool is present, invoke it by name; if absent, surface the install command for the user's IDE and stop until the user installs the MCP". peaks-cli does not install MCPs on the user's behalf as of slice #016.
|
|
27
29
|
|
|
28
30
|
## Stage 3 — Side effect through Peaks CLI only
|
|
29
31
|
|
|
@@ -38,7 +40,7 @@ The skill body must not silently:
|
|
|
38
40
|
- commit or sync intermediate artifacts;
|
|
39
41
|
- create remote repositories.
|
|
40
42
|
|
|
41
|
-
All of these must route through the Peaks CLI under the appropriate command (`peaks
|
|
43
|
+
All of these must route through the Peaks CLI under the appropriate command (`peaks artifacts …`, `peaks memory …`, `peaks openspec …`, `peaks standards …`, `peaks codegraph …`, `peaks capabilities …`), with dry-run preview where supported and `--yes` / `--apply` where a real write is required. The `peaks mcp …` command tree was retired in slice #016; MCP install / dispatch is the LLM runtime's job, not the CLI's.
|
|
42
44
|
|
|
43
45
|
## Allowed in-process references
|
|
44
46
|
|
|
@@ -46,7 +48,7 @@ Some references are not external skills but project-approved utilities and may b
|
|
|
46
48
|
|
|
47
49
|
- `peaks` CLI commands (this binary).
|
|
48
50
|
- `npx`, `npm`, `pnpm`, `yarn`, package managers — only as the underlying mechanism when a `peaks` CLI command spawns them.
|
|
49
|
-
- `
|
|
51
|
+
- `mcp__chrome_devtools__*` — Chrome DevTools MCP tools exposed by the LLM's MCP runtime when the user has installed Chrome DevTools MCP (Claude Code: `claude mcp add chrome-devtools -- npx chrome-devtools-mcp@latest`). Skill bodies tell the LLM to invoke these tools by name when they appear in the tool list. Login / CAPTCHA / SSO / MFA handoff rules and sanitization rules in `browser-workflow.md` still apply.
|
|
50
52
|
|
|
51
53
|
These are not subject to capability discovery because they are part of the Peaks engineering surface, not external skills. The previous `gstack/browse/dist/browse` binary reference is no longer endorsed — see `browser-workflow.md` for the migration recipe.
|
|
52
54
|
|
|
@@ -66,5 +68,5 @@ When a skill body adds a new external reference, it must include the equivalent
|
|
|
66
68
|
1. read the failing skill body section;
|
|
67
69
|
2. identify the external skill or MCP that triggered the failure;
|
|
68
70
|
3. add the capability discovery clause, the reference-only qualifier, the do-not-execute clause, and the Peaks-authoritative gate to that section;
|
|
69
|
-
4. for MCP servers, point the
|
|
71
|
+
4. for MCP servers, point the LLM at the tool-list self-check (its own `mcp__<server>__*` namespace) instead of describing manual `~/.claude/settings.json` edits;
|
|
70
72
|
5. rerun the audit.
|
|
@@ -89,12 +89,14 @@ peaks slice check [--rid <rid>] [--project <path>] [--json]
|
|
|
89
89
|
|
|
90
90
|
这个命令编排:
|
|
91
91
|
1. `npx tsc --noEmit`(typecheck)
|
|
92
|
-
2. `npx vitest run
|
|
92
|
+
2. `npx vitest run --changed`(默认;changed-only suite,只跑 git 改动相关的 test,~1-3s)。要全量请加 `--run-tests`;要彻底跳过请加 `--skip-tests`。
|
|
93
93
|
3. 3-way fan-out(code-review + security-review + perf-baseline)
|
|
94
94
|
4. `peaks workflow verify-pipeline --rid <rid> --project <path>`
|
|
95
95
|
|
|
96
96
|
4 个 check 全绿 + verify-pipeline pass → 才进 `peaks request transition --state qa-handoff`,让 peaks-qa 接管。
|
|
97
97
|
|
|
98
|
+
> **新增 run 017(2026-06-09)**:边界默认走 changed-only suite,原来的全 suite 行为移到 `--run-tests` opt-in。`peaks-solo-test` skill 仍然是手动跑全量的入口。rationale: 全量 30s+ 严重拖慢 workflow;changed-only 命中 99% 真正回归。详见 PRD `.peaks/_runtime/2026-06-07-session-84feb7/prd/requests/002-017-2026-06-09-remove-auto-full-vitest-from-slice-check.md`。
|
|
99
|
+
|
|
98
100
|
## Micro-cycle → 边界 check → QA 的串联
|
|
99
101
|
|
|
100
102
|
```
|
|
@@ -139,7 +141,7 @@ verdict=return-to-rd → RD 修 (new slice 内部走 micro-cycle)
|
|
|
139
141
|
## 为什么这套比当前 peaks-solo 的设计合理
|
|
140
142
|
|
|
141
143
|
- **快**:micro-cycle ~100ms(vs 30s 全 suite),改 10 个 bug 从 5 分钟降到 30 秒
|
|
142
|
-
- **稳**:边界 check 不省,4 项检查(tsc + vitest + 3-way + verify-pipeline
|
|
144
|
+
- **稳**:边界 check 不省,4 项检查(tsc + vitest run --changed + 3-way + verify-pipeline)一次全跑;changed-only 模式 1-3s 内出结果,全量用 `--run-tests` opt-in
|
|
143
145
|
- **清晰**:LLM 看到一个 explicit "禁止" 列表 + 强制 sequence,比"建议"更不容易越界
|
|
144
146
|
- **可观测**:micro-cycle 走单测 → 边界跑 verify-pipeline,每步都有 JSON envelope 验证
|
|
145
147
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
# OpenSpec
|
|
1
|
+
# OpenSpec Lifecycle for Peaks Solo
|
|
2
2
|
|
|
3
|
-
Peaks Solo orchestrates RD, QA, and SC. When the target repository uses OpenSpec
|
|
3
|
+
Peaks Solo orchestrates RD, QA, and SC. When the target repository uses OpenSpec, Solo must drive the full lifecycle through the Peaks CLI so each role works against the same stable surface.
|
|
4
|
+
|
|
5
|
+
> **Slice #016 (2026-06-09)**: this document used to live at `openspec-mcp-workflow.md` and contained a section on the `peaks mcp *` lifecycle. The MCP subsystem was retired in slice #016; that section is gone. The OpenSpec lifecycle described below is unchanged.
|
|
4
6
|
|
|
5
7
|
## OpenSpec change lifecycle
|
|
6
8
|
|
|
@@ -20,23 +22,6 @@ Rules Solo applies:
|
|
|
20
22
|
- `validate` is run twice per change in a refactor flow: once before slicing (RD entry gate) and once before archive (QA exit gate). Both must end with `data.valid === true`.
|
|
21
23
|
- `archive --apply` is the lifecycle terminator; Solo only invokes it after QA acceptance and SC commit.
|
|
22
24
|
|
|
23
|
-
## MCP capability lifecycle
|
|
24
|
-
|
|
25
|
-
```text
|
|
26
|
-
peaks mcp list / scan → Solo inventories what is configured today
|
|
27
|
-
peaks mcp plan → Solo previews the install diff before any write
|
|
28
|
-
peaks mcp apply --yes → Solo authorizes the install (real side effect)
|
|
29
|
-
peaks mcp call → RD or QA invokes a tool on the installed server
|
|
30
|
-
peaks mcp rollback → Solo restores from a peaks-managed backup
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Rules Solo applies:
|
|
34
|
-
|
|
35
|
-
- `apply` is the first real side effect in the MCP track. It requires `--yes`, backs up `~/.claude/settings.json` first, and refuses to overwrite non-peaks-managed entries unless `--claim` is passed. Solo decides whether `--claim` is appropriate.
|
|
36
|
-
- Required env vars must be set in the runtime environment before `apply` or `call`. Peaks refuses to spawn a server with missing env, surfacing each missing key in `envCheck.missing`.
|
|
37
|
-
- `call` writes evidence into the RD or QA artifact. Solo never pastes secrets, full request/response bodies, or session tokens into the handoff capsule.
|
|
38
|
-
- `rollback` is the recovery action when an install or update made things worse. The backup path is the one Peaks reported during `apply`.
|
|
39
|
-
|
|
40
25
|
## Refactor workflow wiring
|
|
41
26
|
|
|
42
27
|
For `peaks-solo refactor` runs against a repository with `openspec/`:
|
|
@@ -46,7 +31,7 @@ For `peaks-solo refactor` runs against a repository with `openspec/`:
|
|
|
46
31
|
3. QA exit gate — re-run `peaks openspec validate <id>` after implementation; record the result in the QA validation report.
|
|
47
32
|
4. Archive — `peaks openspec archive <id> --apply` only after QA passes the exit gate and SC closes the final commit.
|
|
48
33
|
|
|
49
|
-
If
|
|
34
|
+
If the consuming LLM needs an MCP server for docs lookup or research (e.g. Context7), it checks its own tool list for `mcp__<server>__*` and tells the user the install command if absent. peaks-cli is no longer in the install path; the LLM is the executor, the IDE is the dispatcher.
|
|
50
35
|
|
|
51
36
|
## Boundary
|
|
52
37
|
|
|
@@ -143,45 +143,26 @@ While running, call `peaks sub-agent heartbeat --record <dispatchRecordPath>
|
|
|
143
143
|
to keep the user informed during the wait.
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
-
**Slice #
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
`mcp__` invocations (which fail in non-Claude IDEs) or skip MCP
|
|
153
|
-
operations entirely (which breaks RD/QA/UI flows that depend on the
|
|
154
|
-
Playwright, Chrome DevTools, Figma, or Context7 servers).
|
|
146
|
+
**Slice #016 retirement (G3 prompt template — MCP subsystem removed)**:
|
|
147
|
+
The MCP-decouple paragraph from slice #007-007 no longer applies.
|
|
148
|
+
peaks-cli no longer manages MCP install or invocation. Sub-agents
|
|
149
|
+
check their own tool list for `mcp__<server>__*` entries and invoke
|
|
150
|
+
the tool by name directly. The only prompt-template addition that
|
|
151
|
+
remains is the tool-list self-check:
|
|
155
152
|
|
|
156
153
|
```
|
|
157
|
-
|
|
158
|
-
context7)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
- playwright-mcp.browser-validation (headed browser, primary E2E surface)
|
|
165
|
-
- chrome-devtools-mcp.browser-debug (CDP to running Chrome on :9222, secondary)
|
|
166
|
-
- figma-context-mcp.design-context (Figma design data, requires FIGMA_API_KEY)
|
|
167
|
-
- context7.docs-lookup (library docs, requires CONTEXT7_API_KEY)
|
|
168
|
-
|
|
169
|
-
For install / plan / detect, use:
|
|
170
|
-
peaks mcp list --json
|
|
171
|
-
peaks mcp plan --capability <capabilityId> --json
|
|
172
|
-
peaks mcp apply --capability <capabilityId> --yes --json
|
|
173
|
-
|
|
174
|
-
The `peaks mcp plan` envelope's `envCheck.missing` field is the source of
|
|
175
|
-
truth for required env vars. Do not bake the `mcp__<server>__*` prefix
|
|
176
|
-
into any artifact or message; the prefix is owned by the LLM runtime, not
|
|
177
|
-
by the skill. On Trae, `capabilities.mcpInstall` is `false`; do not
|
|
178
|
-
attempt `peaks mcp apply` on Trae — surface the manual install path
|
|
179
|
-
instead.
|
|
154
|
+
This task may need an MCP server (playwright, chrome-devtools, figma,
|
|
155
|
+
or context7). Check your tool list for any `mcp__<server>__*` entry.
|
|
156
|
+
If present, invoke the tool by name directly. If absent, tell the user
|
|
157
|
+
the install command for their IDE (Claude Code:
|
|
158
|
+
`claude mcp add <server> -- <npx-command>`) and stop the sub-task until
|
|
159
|
+
the user installs the MCP. Do NOT hand-edit `.claude/settings.json` or
|
|
160
|
+
auto-install on the user's behalf.
|
|
180
161
|
```
|
|
181
162
|
|
|
182
|
-
The
|
|
183
|
-
|
|
184
|
-
|
|
163
|
+
The tool-list self-check is required for any sub-agent that needs a
|
|
164
|
+
browser, design, or docs-lookup capability. The CLI auto-generates it
|
|
165
|
+
for `role in (rd, qa, ui, txt)`.
|
|
185
166
|
when the active IDE is not `claude-code`; for `role = general-purpose`
|
|
186
167
|
or unknown roles, the caller (the SKILL.md heart of the Dispatcher) must
|
|
187
168
|
add it explicitly.
|
package/skills/peaks-ui/SKILL.md
CHANGED
|
@@ -13,7 +13,7 @@ UI's headed-browser work (visual inspection, regression seed capture, Figma / li
|
|
|
13
13
|
|
|
14
14
|
### Contract 1 — Inspection screenshots must land under .peaks/<sid>/qa/screenshots/
|
|
15
15
|
|
|
16
|
-
Every Playwright screenshot tool call (
|
|
16
|
+
Every Playwright screenshot tool call (the LLM invokes `browser_take_screenshot` directly when the Playwright MCP is present in its tool list) **MUST** pass `filename` (in the args object) inside `.peaks/<session-id>/qa/screenshots/`, named after the inspection target (e.g. `home-after-cta.png`, `empty-state-v2.png`). Do not let Playwright fall back to the project root. After every batch, run:
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
ls .peaks/<sid>/qa/screenshots/*.png 2>&1
|
|
@@ -61,7 +61,7 @@ What the sub-agent **MUST** still do:
|
|
|
61
61
|
- Do NOT call `Skill(skill="...")`.
|
|
62
62
|
- Do NOT call `peaks skill presence:set` — Solo owns the active-skill file.
|
|
63
63
|
- Do NOT modify application code. UI is design-direction only; the actual frontend code is written in the RD implementation phase.
|
|
64
|
-
- Do NOT install MCP servers. If
|
|
64
|
+
- Do NOT install MCP servers. If the LLM tool list does not include the Playwright MCP and the headed browser is required, return `{"status":"blocked","blockedReason":"playwright-mcp-unavailable"}` and let Solo escalate to the user. (peaks-cli no longer manages MCP install — the user runs `claude mcp add playwright -- npx @playwright/mcp@latest` themselves in Claude Code, or the IDE-specific install command otherwise.)
|
|
65
65
|
- Do NOT commit, push, install hooks, or apply settings.json mutations.
|
|
66
66
|
- Do NOT ask the user interactive questions. If you need clarification, return `{"status":"blocked","blockedReason":"<text>"}`.
|
|
67
67
|
|
|
@@ -129,14 +129,13 @@ peaks request init --role ui --id <request-id> --project <repo> --apply --json
|
|
|
129
129
|
peaks request show <request-id> --role prd --project <repo> --json # read linked PRD scope
|
|
130
130
|
|
|
131
131
|
# 2. ensure Playwright MCP is available for the visible browser check
|
|
132
|
-
peaks
|
|
133
|
-
#
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
#
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
# → NEVER route through chrome-devtools-mcp as a browser-launch substitute (it cannot launch)
|
|
132
|
+
# Slice #016: peaks-cli no longer manages MCP install. The LLM checks
|
|
133
|
+
# its own tool list for any Playwright MCP entry in the LLM tool list. If absent, the
|
|
134
|
+
# LLM tells the user the install command (`claude mcp add playwright
|
|
135
|
+
# -- npx @playwright/mcp@latest` in Claude Code) and reports the gate
|
|
136
|
+
# as blocked. Do NOT silently downgrade to screenshots-only, manual
|
|
137
|
+
# steps, or other tools. Do NOT route through chrome-devtools-mcp as a
|
|
138
|
+
# browser-launch substitute (it cannot launch a browser of its own).
|
|
140
139
|
|
|
141
140
|
# 3. read project-scan for component library and CSS framework context
|
|
142
141
|
# check .peaks/<session-id>/rd/project-scan.md (blocking if missing for existing projects)
|
|
@@ -151,8 +150,8 @@ peaks mcp apply --capability playwright-mcp.browser-validation --yes --json
|
|
|
151
150
|
# See "Prototype fidelity gate" section for the full decision tree.
|
|
152
151
|
|
|
153
152
|
# 5. drive the running page or prototype through Claude Code MCP tools
|
|
154
|
-
# (
|
|
155
|
-
#
|
|
153
|
+
# (the LLM invokes these directly from its tool list — no peaks-cli envelope)
|
|
154
|
+
# browser_navigate --args '{"url":"<url>"}'
|
|
156
155
|
# → URL (after allow-list check), launches headed browser
|
|
157
156
|
#
|
|
158
157
|
# LOGIN GATE (MANDATORY checkpoint):
|
|
@@ -162,19 +161,18 @@ peaks mcp apply --capability playwright-mcp.browser-validation --yes --json
|
|
|
162
161
|
# If user does not confirm within reasonable time → pause and ask.
|
|
163
162
|
# Only after user confirmation, continue to:
|
|
164
163
|
#
|
|
165
|
-
#
|
|
164
|
+
# browser_take_screenshot --args '{"filename":"<abs-path>"}'
|
|
166
165
|
# → visible-browser confirmation
|
|
167
|
-
#
|
|
166
|
+
# browser_snapshot --args '{}'
|
|
168
167
|
# → accessibility tree for regression seeds
|
|
169
|
-
#
|
|
168
|
+
# browser_console_messages --args '{}'
|
|
170
169
|
# → console errors
|
|
171
|
-
#
|
|
170
|
+
# browser_network_requests --args '{}'
|
|
172
171
|
# → failed network
|
|
173
|
-
#
|
|
172
|
+
# browser_close --args '{}'
|
|
174
173
|
# → end the session cleanly
|
|
175
|
-
# The skill body NEVER bakes in the
|
|
176
|
-
# resolves the tool name from the registered server.
|
|
177
|
-
# `playwright-mcp.browser-validation` is the contract; the registry is the source of truth.
|
|
174
|
+
# The skill body NEVER bakes in the the Playwright MCP tools prefix; the LLM's runtime
|
|
175
|
+
# resolves the tool name from the registered server.
|
|
178
176
|
|
|
179
177
|
# 5. write design-draft artifact to .peaks/<session-id>/ui/design-draft.md
|
|
180
178
|
|
|
@@ -238,7 +236,7 @@ Use gstack as a concrete design-review workflow reference for the `Plan → Revi
|
|
|
238
236
|
- map browser walkthrough concepts to UI regression seeds when runtime validation is approved;
|
|
239
237
|
- keep accessibility, performance, and product-specific visual direction as Peaks-Cli UI acceptance inputs.
|
|
240
238
|
|
|
241
|
-
For frontend work, especially full-auto mode, use Playwright MCP to inspect the running page or prototype before accepting the UI direction. The
|
|
239
|
+
For frontend work, especially full-auto mode, use the Playwright MCP to inspect the running page or prototype before accepting the UI direction. The LLM checks its own tool list for any Playwright MCP entry in the LLM tool list; if present, it invokes the tools by name directly (browser_navigate / browser_snapshot / browser_take_screenshot / browser_console_messages / browser_network_requests / browser_close) — there is no peaks-cli envelope. Playwright MCP launches a headed browser on demand; if the tool list is empty, the user installs via `claude mcp add playwright -- npx @playwright/mcp@latest` (Claude Code) or the IDE's own MCP install path. (Chrome DevTools MCP is a secondary surface that connects to an already-running Chrome via `--remote-debugging-port=9222`; it does NOT launch a browser on its own.) If login, CAPTCHA, SSO, or MFA appears, the visible browser is already open; wait for the user to complete login and explicitly confirm completion before continuing. Capture only sanitized visible regressions, weak hierarchy, generic template patterns, console errors, and interaction problems as UI feedback that should return to design/RD before handing off to QA; do not retain login URLs, cookies, headers, tokens, storage state, browser traces, or screenshots/logs containing PII or SSO/MFA material. Canonical browser workflow: `peaks-solo/references/browser-workflow.md`.
|
|
242
240
|
|
|
243
241
|
## Prototype fidelity gate (MANDATORY — check BEFORE any design work)
|
|
244
242
|
|
|
@@ -248,7 +246,7 @@ For frontend work, especially full-auto mode, use Playwright MCP to inspect the
|
|
|
248
246
|
|
|
249
247
|
Check these sources in order:
|
|
250
248
|
|
|
251
|
-
1. **Figma design file** — If the PRD links to a Figma file,
|
|
249
|
+
1. **Figma design file** — If the PRD links to a Figma file, the LLM checks its own tool list for any the Figma MCP entry. If present, it invokes `get_figma_data` (or similar) directly with `{"fileKey":"<key>"}`; `FIGMA_API_KEY` must be set in the user's env for the MCP to authenticate. The skill body never bakes in the Figma MCP prefix; the LLM's runtime owns the namespace. The Figma data IS the design. Replicate layout, spacing, colors, typography, and component choices exactly as specified.
|
|
252
250
|
2. **PRD document screenshots** — If the PRD source (Feishu/Lark doc) contains screenshots or mockups, those ARE the visual target. Check `.peaks/<id>/prd/source/` for saved screenshots.
|
|
253
251
|
3. **PRD visual descriptions** — If the PRD explicitly describes layout, component placement, or visual behavior, those descriptions are constraints, not suggestions.
|
|
254
252
|
4. **Existing application pages** — If modifying an existing app, the existing visual language (component library, spacing patterns, color usage) is the fidelity baseline. New pages must match existing conventions.
|
|
@@ -355,8 +353,8 @@ Use `peaks capabilities --source access-repo --json` and `peaks capabilities --s
|
|
|
355
353
|
|
|
356
354
|
- In full-auto frontend mode, prefer the `awesome-design-md` + `taste-skill`/`design-taste-frontend` combination before shadcn/ui or generic component-library output (capability discovery must confirm availability first).
|
|
357
355
|
- shadcn/ui, React Bits, awesome-design-md, taste-skill, and ui-ux-pro-max-skill are UI references; do not treat unreviewed generated UI as finished design.
|
|
358
|
-
- Chrome DevTools MCP and Agent Browser can support runtime UI inspection only after the user approves the app target.
|
|
359
|
-
- Figma Context MCP and Penpot require user-authorized design access and must not persist tokens or private design data in project artifacts. Same
|
|
356
|
+
- Chrome DevTools MCP and Agent Browser can support runtime UI inspection only after the user approves the app target. (Slice #016: peaks-cli no longer auto-installs these; the user runs the IDE-native MCP install command themselves, and the LLM invokes the tool by name from its tool list when present.)
|
|
357
|
+
- Figma Context MCP and Penpot require user-authorized design access and must not persist tokens or private design data in project artifacts. Same rule: the LLM's tool list is the source of truth; peaks-cli is not in the install path.
|
|
360
358
|
- Check license, accessibility, and performance before translating external visual references into Peaks-Cli UI constraints.
|
|
361
359
|
|
|
362
360
|
## Boundaries
|
|
@@ -11,7 +11,7 @@ Use this path before generating or accepting frontend UI:
|
|
|
11
11
|
3. Produce a concrete visual direction, not vague “clean modern” language.
|
|
12
12
|
4. Reject generic AI UI tells: centered stock hero, uniform card grids, default shadcn/library styling, purple-blue gradients, three equal feature cards, generic placeholder copy, and static-only happy states.
|
|
13
13
|
5. Require meaningful loading, empty, error, hover, focus, active, and responsive states.
|
|
14
|
-
6. Use Playwright MCP on the running page or prototype to inspect real browser output (
|
|
14
|
+
6. Use Playwright MCP on the running page or prototype to inspect real browser output (the LLM checks its tool list for `mcp__playwright__*`; if absent, the user installs via `claude mcp add playwright -- npx @playwright/mcp@latest` for Claude Code, or the IDE-native MCP install command otherwise — peaks-cli no longer auto-installs as of slice #016; open with `browser_navigate` / `navigate_page`, capture with `browser_snapshot` and `browser_take_screenshot`); visible browser confirmation is mandatory, and login/CAPTCHA/SSO/MFA requires waiting for explicit user confirmation before continuing.
|
|
15
15
|
7. If the browser view looks generic, visually weak, broken, inaccessible, or has console/runtime errors, return to design/RD and iterate before handing off to QA.
|
|
16
16
|
|
|
17
17
|
## Outputs
|
|
@@ -21,7 +21,7 @@ Use this path before generating or accepting frontend UI:
|
|
|
21
21
|
- visual direction with references;
|
|
22
22
|
- design dials and rejected generic patterns;
|
|
23
23
|
- interaction constraints;
|
|
24
|
-
- Playwright MCP browser observations when frontend output exists (`
|
|
24
|
+
- Playwright MCP browser observations when frontend output exists (`browser_snapshot`, `browser_take_screenshot`, `browser_console_messages`, `browser_network_requests`);
|
|
25
25
|
- UI regression seeds;
|
|
26
26
|
- accessibility notes;
|
|
27
27
|
- taste report.
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { InvalidArgumentError } from 'commander';
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
3
|
-
import { scanMcpServers } from '../../services/mcp/mcp-scan-service.js';
|
|
4
|
-
import { planMcpInstall } from '../../services/mcp/mcp-plan-service.js';
|
|
5
|
-
import { applyMcpInstall, rollbackMcpInstall } from '../../services/mcp/mcp-apply-service.js';
|
|
6
|
-
import { callMcpTool } from '../../services/mcp/mcp-call-service.js';
|
|
7
|
-
import { createStdioTransportFromSpec } from '../../services/mcp/mcp-stdio-transport.js';
|
|
8
|
-
import { fail, ok } from '../../shared/result.js';
|
|
9
|
-
import { addJsonOption, failUnsupportedNonDryRun, getErrorMessage, printResult } from '../cli-helpers.js';
|
|
10
|
-
function parsePositiveInteger(value) {
|
|
11
|
-
if (!/^\d+$/.test(value)) {
|
|
12
|
-
throw new InvalidArgumentError('must be a positive integer');
|
|
13
|
-
}
|
|
14
|
-
const parsed = Number(value);
|
|
15
|
-
if (!Number.isSafeInteger(parsed) || parsed < 1) {
|
|
16
|
-
throw new InvalidArgumentError('must be a positive integer');
|
|
17
|
-
}
|
|
18
|
-
return parsed;
|
|
19
|
-
}
|
|
20
|
-
async function resolveCallArgs(options) {
|
|
21
|
-
if (options.argsJson !== undefined && options.args !== undefined) {
|
|
22
|
-
throw new Error('Pass either --args-json or --args, not both');
|
|
23
|
-
}
|
|
24
|
-
const raw = options.argsJson !== undefined
|
|
25
|
-
? options.argsJson
|
|
26
|
-
: options.args !== undefined ? await readFile(options.args, 'utf8') : '{}';
|
|
27
|
-
const parsed = JSON.parse(raw);
|
|
28
|
-
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
29
|
-
throw new Error('MCP tool arguments must be a JSON object');
|
|
30
|
-
}
|
|
31
|
-
return parsed;
|
|
32
|
-
}
|
|
33
|
-
export function registerMcpCommands(program, io) {
|
|
34
|
-
const mcp = program.command('mcp').description('Manage Claude Code MCP servers');
|
|
35
|
-
addJsonOption(mcp
|
|
36
|
-
.command('list')
|
|
37
|
-
.alias('scan')
|
|
38
|
-
.description('Scan Claude Code settings for configured MCP servers')
|
|
39
|
-
.option('--project <path>', 'project root to also scan project-level .claude/settings.json')).action(async (options) => {
|
|
40
|
-
try {
|
|
41
|
-
const report = await scanMcpServers(options.project !== undefined ? { projectRoot: options.project } : {});
|
|
42
|
-
printResult(io, ok('mcp.list', report), options.json);
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
printResult(io, fail('mcp.list', 'MCP_LIST_FAILED', getErrorMessage(error), {}, ['Check Claude settings path and permissions before retrying']), options.json);
|
|
46
|
-
process.exitCode = 1;
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
addJsonOption(mcp
|
|
50
|
-
.command('plan')
|
|
51
|
-
.description('Plan an MCP server install diff for a capability (dry-run only)')
|
|
52
|
-
.requiredOption('--capability <id>', 'capability id from the MCP install registry')
|
|
53
|
-
.option('--project <path>', 'project root for scoped scan')
|
|
54
|
-
.option('--dry-run', 'preview the install diff (always true)', true)
|
|
55
|
-
.option('--no-dry-run', 'unsupported: peaks mcp plan never writes settings')).action(async (options) => {
|
|
56
|
-
if (options.dryRun === false) {
|
|
57
|
-
failUnsupportedNonDryRun(io, 'mcp.plan', options.json);
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
try {
|
|
61
|
-
const planOptions = options.project !== undefined ? { projectRoot: options.project } : {};
|
|
62
|
-
const plan = await planMcpInstall(options.capability, planOptions);
|
|
63
|
-
if (plan.action === 'unknown-capability') {
|
|
64
|
-
printResult(io, fail('mcp.plan', 'MCP_UNKNOWN_CAPABILITY', `No MCP install spec registered for capability ${options.capability}`, plan, plan.nextActions), options.json);
|
|
65
|
-
process.exitCode = 1;
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
printResult(io, ok('mcp.plan', plan, [], plan.nextActions), options.json);
|
|
69
|
-
}
|
|
70
|
-
catch (error) {
|
|
71
|
-
printResult(io, fail('mcp.plan', 'MCP_PLAN_FAILED', getErrorMessage(error), { capabilityId: options.capability }, ['Check Claude settings path and the capability id before retrying']), options.json);
|
|
72
|
-
process.exitCode = 1;
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
addJsonOption(mcp
|
|
76
|
-
.command('apply')
|
|
77
|
-
.description('Apply an MCP server install for a capability (writes .claude/settings.json with backup)')
|
|
78
|
-
.requiredOption('--capability <id>', 'capability id from the MCP install registry')
|
|
79
|
-
.option('--yes', 'confirm the write — required for any real side effect')
|
|
80
|
-
.option('--claim', 'take ownership of an existing non-peaks-managed server entry')
|
|
81
|
-
.option('--project <path>', 'project root for scoped scan')).action(async (options) => {
|
|
82
|
-
if (options.yes !== true) {
|
|
83
|
-
printResult(io, fail('mcp.apply', 'MCP_APPLY_REQUIRES_YES', 'Refusing to apply without --yes', { capabilityId: options.capability }, ['Re-run with --yes to confirm the write']), options.json);
|
|
84
|
-
process.exitCode = 1;
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
try {
|
|
88
|
-
const applyOptions = {};
|
|
89
|
-
if (options.project !== undefined) {
|
|
90
|
-
applyOptions.projectRoot = options.project;
|
|
91
|
-
}
|
|
92
|
-
if (options.claim === true) {
|
|
93
|
-
applyOptions.claim = true;
|
|
94
|
-
}
|
|
95
|
-
const result = await applyMcpInstall(options.capability, applyOptions);
|
|
96
|
-
printResult(io, ok('mcp.apply', result), options.json);
|
|
97
|
-
}
|
|
98
|
-
catch (error) {
|
|
99
|
-
printResult(io, fail('mcp.apply', 'MCP_APPLY_FAILED', getErrorMessage(error), { capabilityId: options.capability }, ['Check the plan first with peaks mcp plan, then re-run apply']), options.json);
|
|
100
|
-
process.exitCode = 1;
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
addJsonOption(mcp
|
|
104
|
-
.command('rollback')
|
|
105
|
-
.description('Restore Claude Code settings.json from a peaks-managed MCP backup file')
|
|
106
|
-
.requiredOption('--backup <path>', 'path to a previously created backup settings.json')).action(async (options) => {
|
|
107
|
-
try {
|
|
108
|
-
const result = await rollbackMcpInstall({ backupPath: options.backup });
|
|
109
|
-
printResult(io, ok('mcp.rollback', result), options.json);
|
|
110
|
-
}
|
|
111
|
-
catch (error) {
|
|
112
|
-
printResult(io, fail('mcp.rollback', 'MCP_ROLLBACK_FAILED', getErrorMessage(error), { backupPath: options.backup }, ['Verify the backup path and rerun']), options.json);
|
|
113
|
-
process.exitCode = 1;
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
addJsonOption(mcp
|
|
117
|
-
.command('call')
|
|
118
|
-
.description('Invoke a tool on an installed MCP server via stdio (spawns the server, calls tools/call, closes)')
|
|
119
|
-
.requiredOption('--capability <id>', 'capability id from the MCP install registry')
|
|
120
|
-
.requiredOption('--tool <name>', 'MCP tool name to invoke')
|
|
121
|
-
.option('--args <path>', 'path to a JSON file describing the tool arguments object')
|
|
122
|
-
.option('--args-json <jsonString>', 'inline JSON object describing the tool arguments')
|
|
123
|
-
.option('--timeout <ms>', 'per-request timeout in milliseconds', parsePositiveInteger)).action(async (options) => {
|
|
124
|
-
try {
|
|
125
|
-
const args = await resolveCallArgs(options);
|
|
126
|
-
const factory = createStdioTransportFromSpec;
|
|
127
|
-
const callOptions = {
|
|
128
|
-
capabilityId: options.capability,
|
|
129
|
-
toolName: options.tool,
|
|
130
|
-
args,
|
|
131
|
-
transportFactory: factory
|
|
132
|
-
};
|
|
133
|
-
if (options.timeout !== undefined) {
|
|
134
|
-
callOptions.timeoutMs = Number(options.timeout);
|
|
135
|
-
}
|
|
136
|
-
const result = await callMcpTool(callOptions);
|
|
137
|
-
printResult(io, ok('mcp.call', result), options.json);
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
printResult(io, fail('mcp.call', 'MCP_CALL_FAILED', getErrorMessage(error), { capabilityId: options.capability, toolName: options.tool }, ['Check the capability id, tool name, args JSON, and required env vars before retrying']), options.json);
|
|
141
|
-
process.exitCode = 1;
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { type PlanMcpInstallOptions, type McpInstallEnvCheck } from './mcp-plan-service.js';
|
|
2
|
-
export type McpApplyAction = 'add' | 'update' | 'claimed' | 'noop';
|
|
3
|
-
export type McpApplyBackupInfo = {
|
|
4
|
-
path: string | null;
|
|
5
|
-
skipped: boolean;
|
|
6
|
-
};
|
|
7
|
-
export type McpApplyResult = {
|
|
8
|
-
capabilityId: string;
|
|
9
|
-
action: McpApplyAction;
|
|
10
|
-
backup: McpApplyBackupInfo;
|
|
11
|
-
written: {
|
|
12
|
-
settingsPath: string;
|
|
13
|
-
managedMarkerPath: string;
|
|
14
|
-
};
|
|
15
|
-
envCheck: McpInstallEnvCheck;
|
|
16
|
-
};
|
|
17
|
-
export type McpApplyOptions = PlanMcpInstallOptions & {
|
|
18
|
-
claim?: boolean;
|
|
19
|
-
backupRoot?: string;
|
|
20
|
-
clock?: () => string;
|
|
21
|
-
};
|
|
22
|
-
export type McpRollbackOptions = {
|
|
23
|
-
backupPath: string;
|
|
24
|
-
globalSettingsPath?: string;
|
|
25
|
-
};
|
|
26
|
-
export type McpRollbackResult = {
|
|
27
|
-
restoredFrom: string;
|
|
28
|
-
restoredTo: string;
|
|
29
|
-
};
|
|
30
|
-
export declare function applyMcpInstall(capabilityId: string, options?: McpApplyOptions): Promise<McpApplyResult>;
|
|
31
|
-
export declare function rollbackMcpInstall(options: McpRollbackOptions): Promise<McpRollbackResult>;
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { homedir } from 'node:os';
|
|
2
|
-
import { dirname, join } from 'node:path';
|
|
3
|
-
import { mkdir, writeFile } from 'node:fs/promises';
|
|
4
|
-
import { pathExists, readText } from '../../shared/fs.js';
|
|
5
|
-
import { planMcpInstall } from './mcp-plan-service.js';
|
|
6
|
-
function defaultGlobalSettingsPath() {
|
|
7
|
-
return join(homedir(), '.claude', 'settings.json');
|
|
8
|
-
}
|
|
9
|
-
function defaultManagedMarkerPath() {
|
|
10
|
-
return join(homedir(), '.peaks', 'mcp-managed.json');
|
|
11
|
-
}
|
|
12
|
-
function defaultBackupRoot() {
|
|
13
|
-
return join(homedir(), '.peaks-artifacts', 'mcp-backups');
|
|
14
|
-
}
|
|
15
|
-
function defaultClock() {
|
|
16
|
-
return new Date().toISOString().replace(/[:.]/g, '-');
|
|
17
|
-
}
|
|
18
|
-
async function readJsonFile(path) {
|
|
19
|
-
if (!(await pathExists(path))) {
|
|
20
|
-
return {};
|
|
21
|
-
}
|
|
22
|
-
const raw = await readText(path);
|
|
23
|
-
if (raw.length === 0) {
|
|
24
|
-
return {};
|
|
25
|
-
}
|
|
26
|
-
const parsed = JSON.parse(raw);
|
|
27
|
-
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
28
|
-
return {};
|
|
29
|
-
}
|
|
30
|
-
return parsed;
|
|
31
|
-
}
|
|
32
|
-
async function writeJsonFile(path, data) {
|
|
33
|
-
await mkdir(dirname(path), { recursive: true });
|
|
34
|
-
await writeFile(path, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
|
|
35
|
-
}
|
|
36
|
-
function buildServerConfig(spec) {
|
|
37
|
-
const env = {};
|
|
38
|
-
for (const key of spec.envKeys) {
|
|
39
|
-
env[key] = `\${${key}}`;
|
|
40
|
-
}
|
|
41
|
-
return { command: spec.command, args: [...spec.args], env };
|
|
42
|
-
}
|
|
43
|
-
async function createBackup(settingsPath, backupRoot, timestamp) {
|
|
44
|
-
if (!(await pathExists(settingsPath))) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
const backupPath = join(backupRoot, timestamp, 'settings.json');
|
|
48
|
-
await mkdir(dirname(backupPath), { recursive: true });
|
|
49
|
-
await writeFile(backupPath, await readText(settingsPath), 'utf8');
|
|
50
|
-
return backupPath;
|
|
51
|
-
}
|
|
52
|
-
async function updateManagedMarker(markerPath, name) {
|
|
53
|
-
const marker = await readJsonFile(markerPath);
|
|
54
|
-
const existing = Array.isArray(marker.servers)
|
|
55
|
-
? marker.servers.filter((entry) => typeof entry === 'string')
|
|
56
|
-
: [];
|
|
57
|
-
if (!existing.includes(name)) {
|
|
58
|
-
existing.push(name);
|
|
59
|
-
}
|
|
60
|
-
await writeJsonFile(markerPath, { ...marker, servers: existing });
|
|
61
|
-
}
|
|
62
|
-
export async function applyMcpInstall(capabilityId, options = {}) {
|
|
63
|
-
const plan = await planMcpInstall(capabilityId, options);
|
|
64
|
-
if (plan.action === 'unknown-capability' || plan.spec === null) {
|
|
65
|
-
throw new Error(`No MCP install spec registered for capability ${capabilityId} (unknown-capability)`);
|
|
66
|
-
}
|
|
67
|
-
if (plan.envCheck.missing.length > 0) {
|
|
68
|
-
throw new Error(`Refusing to apply: missing required env vars: ${plan.envCheck.missing.join(', ')}`);
|
|
69
|
-
}
|
|
70
|
-
if (plan.action === 'conflict' && options.claim !== true) {
|
|
71
|
-
throw new Error(`Refusing to apply: server ${plan.spec.name} exists but is not peaks-managed (conflict). Re-run with --claim to take ownership.`);
|
|
72
|
-
}
|
|
73
|
-
const settingsPath = options.globalSettingsPath ?? defaultGlobalSettingsPath();
|
|
74
|
-
const markerPath = options.managedMarkerPath ?? defaultManagedMarkerPath();
|
|
75
|
-
const backupRoot = options.backupRoot ?? defaultBackupRoot();
|
|
76
|
-
const clock = options.clock ?? defaultClock;
|
|
77
|
-
if (plan.action === 'noop') {
|
|
78
|
-
return {
|
|
79
|
-
capabilityId,
|
|
80
|
-
action: 'noop',
|
|
81
|
-
backup: { path: null, skipped: true },
|
|
82
|
-
written: { settingsPath, managedMarkerPath: markerPath },
|
|
83
|
-
envCheck: plan.envCheck
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
const backupPath = await createBackup(settingsPath, backupRoot, clock());
|
|
87
|
-
const settings = await readJsonFile(settingsPath);
|
|
88
|
-
const existingServers = settings.mcpServers !== null && typeof settings.mcpServers === 'object' && !Array.isArray(settings.mcpServers)
|
|
89
|
-
? settings.mcpServers
|
|
90
|
-
: {};
|
|
91
|
-
const nextServers = { ...existingServers, [plan.spec.name]: buildServerConfig(plan.spec) };
|
|
92
|
-
await writeJsonFile(settingsPath, { ...settings, mcpServers: nextServers });
|
|
93
|
-
await updateManagedMarker(markerPath, plan.spec.name);
|
|
94
|
-
const action = plan.action === 'conflict' ? 'claimed' : plan.action;
|
|
95
|
-
return {
|
|
96
|
-
capabilityId,
|
|
97
|
-
action,
|
|
98
|
-
backup: { path: backupPath, skipped: false },
|
|
99
|
-
written: { settingsPath, managedMarkerPath: markerPath },
|
|
100
|
-
envCheck: plan.envCheck
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
export async function rollbackMcpInstall(options) {
|
|
104
|
-
if (!(await pathExists(options.backupPath))) {
|
|
105
|
-
throw new Error(`Refusing to rollback: backup file not found at ${options.backupPath}`);
|
|
106
|
-
}
|
|
107
|
-
const target = options.globalSettingsPath ?? defaultGlobalSettingsPath();
|
|
108
|
-
const content = await readText(options.backupPath);
|
|
109
|
-
await mkdir(dirname(target), { recursive: true });
|
|
110
|
-
await writeFile(target, content, 'utf8');
|
|
111
|
-
return { restoredFrom: options.backupPath, restoredTo: target };
|
|
112
|
-
}
|