peaks-cli 1.3.5 → 1.3.6

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 (43) hide show
  1. package/dist/src/cli/program.js +0 -2
  2. package/dist/src/services/dashboard/project-dashboard-service.d.ts +0 -7
  3. package/dist/src/services/dashboard/project-dashboard-service.js +1 -8
  4. package/dist/src/services/ide/adapters/claude-code-adapter.js +0 -1
  5. package/dist/src/services/ide/adapters/trae-adapter.js +0 -13
  6. package/dist/src/services/ide/ide-types.d.ts +0 -2
  7. package/dist/src/shared/version.d.ts +1 -1
  8. package/dist/src/shared/version.js +1 -1
  9. package/package.json +1 -1
  10. package/skills/peaks-prd/SKILL.md +16 -16
  11. package/skills/peaks-prd/references/workflow.md +4 -4
  12. package/skills/peaks-qa/SKILL.md +25 -32
  13. package/skills/peaks-qa/references/regression-gates.md +1 -1
  14. package/skills/peaks-rd/SKILL.md +8 -6
  15. package/skills/peaks-rd/references/{openspec-mcp-cli.md → openspec-cli.md} +11 -14
  16. package/skills/peaks-solo/SKILL.md +1 -1
  17. package/skills/peaks-solo/references/a2a-artifact-mapping.md +1 -1
  18. package/skills/peaks-solo/references/browser-workflow.md +49 -38
  19. package/skills/peaks-solo/references/external-skill-invocation.md +9 -7
  20. package/skills/peaks-solo/references/{openspec-mcp-workflow.md → openspec-workflow.md} +5 -20
  21. package/skills/peaks-solo/references/sub-agent-dispatch.md +16 -35
  22. package/skills/peaks-ui/SKILL.md +22 -24
  23. package/skills/peaks-ui/references/workflow.md +2 -2
  24. package/dist/src/cli/commands/mcp-commands.d.ts +0 -3
  25. package/dist/src/cli/commands/mcp-commands.js +0 -144
  26. package/dist/src/services/mcp/mcp-apply-service.d.ts +0 -31
  27. package/dist/src/services/mcp/mcp-apply-service.js +0 -112
  28. package/dist/src/services/mcp/mcp-call-service.d.ts +0 -17
  29. package/dist/src/services/mcp/mcp-call-service.js +0 -34
  30. package/dist/src/services/mcp/mcp-client-service.d.ts +0 -14
  31. package/dist/src/services/mcp/mcp-client-service.js +0 -49
  32. package/dist/src/services/mcp/mcp-install-registry.d.ts +0 -11
  33. package/dist/src/services/mcp/mcp-install-registry.js +0 -38
  34. package/dist/src/services/mcp/mcp-plan-service.d.ts +0 -29
  35. package/dist/src/services/mcp/mcp-plan-service.js +0 -109
  36. package/dist/src/services/mcp/mcp-protocol.d.ts +0 -24
  37. package/dist/src/services/mcp/mcp-protocol.js +0 -41
  38. package/dist/src/services/mcp/mcp-scan-service.d.ts +0 -8
  39. package/dist/src/services/mcp/mcp-scan-service.js +0 -214
  40. package/dist/src/services/mcp/mcp-stdio-transport.d.ts +0 -10
  41. package/dist/src/services/mcp/mcp-stdio-transport.js +0 -50
  42. package/dist/src/services/mcp/mcp-types.d.ts +0 -31
  43. package/dist/src/services/mcp/mcp-types.js +0 -1
@@ -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
- }
@@ -1,17 +0,0 @@
1
- import { type McpInstallSpec } from './mcp-install-registry.js';
2
- import { type McpClientTransport } from './mcp-client-service.js';
3
- export type McpCallTransportFactory = (spec: McpInstallSpec, env: Record<string, string | undefined>) => McpClientTransport;
4
- export type McpCallOptions = {
5
- capabilityId: string;
6
- toolName: string;
7
- transportFactory: McpCallTransportFactory;
8
- args?: Record<string, unknown>;
9
- env?: Record<string, string | undefined>;
10
- timeoutMs?: number;
11
- };
12
- export type McpCallResult = {
13
- capabilityId: string;
14
- toolName: string;
15
- result: unknown;
16
- };
17
- export declare function callMcpTool(options: McpCallOptions): Promise<McpCallResult>;
@@ -1,34 +0,0 @@
1
- import { findMcpInstallSpec } from './mcp-install-registry.js';
2
- import { createMcpClient } from './mcp-client-service.js';
3
- function checkRequiredEnv(spec, env) {
4
- return spec.envKeys.filter((key) => {
5
- const value = env[key];
6
- return value === undefined || value.length === 0;
7
- });
8
- }
9
- export async function callMcpTool(options) {
10
- const spec = findMcpInstallSpec(options.capabilityId);
11
- if (spec === null) {
12
- throw new Error(`No MCP install spec registered for capability ${options.capabilityId}`);
13
- }
14
- const env = options.env ?? process.env;
15
- const missing = checkRequiredEnv(spec, env);
16
- if (missing.length > 0) {
17
- throw new Error(`Refusing to call ${spec.name}: missing required env vars: ${missing.join(', ')}`);
18
- }
19
- const transport = options.transportFactory(spec, env);
20
- const clientOptions = options.timeoutMs !== undefined
21
- ? { transport, timeoutMs: options.timeoutMs }
22
- : { transport };
23
- const client = createMcpClient(clientOptions);
24
- try {
25
- const result = await client.request('tools/call', {
26
- name: options.toolName,
27
- arguments: options.args ?? {}
28
- });
29
- return { capabilityId: options.capabilityId, toolName: options.toolName, result };
30
- }
31
- finally {
32
- await client.close();
33
- }
34
- }
@@ -1,14 +0,0 @@
1
- export type McpClientTransport = {
2
- send: (line: string) => Promise<void>;
3
- onLine: (handler: (line: string) => void) => void;
4
- close: () => Promise<void>;
5
- };
6
- export type McpClientOptions = {
7
- transport: McpClientTransport;
8
- timeoutMs?: number;
9
- };
10
- export type McpClientHandle = {
11
- request: (method: string, params?: unknown) => Promise<unknown>;
12
- close: () => Promise<void>;
13
- };
14
- export declare function createMcpClient(options: McpClientOptions): McpClientHandle;
@@ -1,49 +0,0 @@
1
- import { buildRequest, parseMessages, serializeMessage } from './mcp-protocol.js';
2
- const DEFAULT_TIMEOUT_MS = 30000;
3
- export function createMcpClient(options) {
4
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
5
- let nextId = 1;
6
- let buffer = '';
7
- const pending = new Map();
8
- function deliver(message) {
9
- const entry = pending.get(message.id);
10
- if (entry === undefined) {
11
- return;
12
- }
13
- pending.delete(message.id);
14
- clearTimeout(entry.timer);
15
- if (message.error !== undefined) {
16
- entry.reject(new Error(`MCP error ${message.error.code}: ${message.error.message}`));
17
- return;
18
- }
19
- entry.resolve(message.result);
20
- }
21
- options.transport.onLine((line) => {
22
- buffer += line;
23
- const { messages, remainder } = parseMessages(buffer);
24
- buffer = remainder;
25
- for (const message of messages) {
26
- deliver(message);
27
- }
28
- });
29
- async function request(method, params) {
30
- const id = nextId++;
31
- const message = serializeMessage(buildRequest(id, method, params));
32
- return new Promise((resolve, reject) => {
33
- const timer = setTimeout(() => {
34
- pending.delete(id);
35
- reject(new Error(`MCP request ${method} timed out after ${timeoutMs}ms`));
36
- }, timeoutMs);
37
- pending.set(id, { resolve, reject, timer });
38
- options.transport.send(message).catch((error) => {
39
- clearTimeout(timer);
40
- pending.delete(id);
41
- reject(error instanceof Error ? error : new Error(String(error)));
42
- });
43
- });
44
- }
45
- async function close() {
46
- await options.transport.close();
47
- }
48
- return { request, close };
49
- }
@@ -1,11 +0,0 @@
1
- import type { McpServerScope } from './mcp-types.js';
2
- export type McpInstallSpec = {
3
- capabilityId: string;
4
- name: string;
5
- scope: McpServerScope;
6
- command: string;
7
- args: string[];
8
- envKeys: string[];
9
- };
10
- export declare const seedMcpInstalls: ReadonlyArray<McpInstallSpec>;
11
- export declare function findMcpInstallSpec(capabilityId: string): McpInstallSpec | null;
@@ -1,38 +0,0 @@
1
- export const seedMcpInstalls = [
2
- {
3
- capabilityId: 'context7.docs-lookup',
4
- name: 'context7',
5
- scope: 'global',
6
- command: 'npx',
7
- args: ['-y', '@upstash/context7-mcp@latest'],
8
- envKeys: ['CONTEXT7_API_KEY']
9
- },
10
- {
11
- capabilityId: 'playwright-mcp.browser-validation',
12
- name: 'playwright',
13
- scope: 'global',
14
- command: 'npx',
15
- args: ['-y', '@playwright/mcp@latest'],
16
- envKeys: []
17
- },
18
- {
19
- capabilityId: 'chrome-devtools-mcp.browser-debug',
20
- name: 'chrome-devtools',
21
- scope: 'global',
22
- command: 'npx',
23
- args: ['-y', 'chrome-devtools-mcp@latest'],
24
- envKeys: []
25
- },
26
- {
27
- capabilityId: 'figma-context-mcp.design-context',
28
- name: 'figma',
29
- scope: 'global',
30
- command: 'npx',
31
- args: ['-y', 'figma-developer-mcp@latest', '--stdio'],
32
- envKeys: ['FIGMA_API_KEY']
33
- }
34
- ];
35
- export function findMcpInstallSpec(capabilityId) {
36
- const match = seedMcpInstalls.find((spec) => spec.capabilityId === capabilityId);
37
- return match ?? null;
38
- }
@@ -1,29 +0,0 @@
1
- import { type McpScanOptions } from './mcp-scan-service.js';
2
- import { type McpInstallSpec } from './mcp-install-registry.js';
3
- import type { McpServerConfig } from './mcp-types.js';
4
- export type McpInstallAction = 'add' | 'update' | 'noop' | 'conflict' | 'unknown-capability';
5
- export type McpInstallEnvCheck = {
6
- missing: string[];
7
- };
8
- export type McpInstallDiffEntry<T> = {
9
- before: T;
10
- after: T;
11
- };
12
- export type McpInstallDiff = {
13
- command?: McpInstallDiffEntry<string>;
14
- args?: McpInstallDiffEntry<string[]>;
15
- envKeys?: McpInstallDiffEntry<string[]>;
16
- };
17
- export type McpInstallPlan = {
18
- capabilityId: string;
19
- action: McpInstallAction;
20
- spec: McpInstallSpec | null;
21
- current: McpServerConfig | null;
22
- envCheck: McpInstallEnvCheck;
23
- diff: McpInstallDiff | null;
24
- nextActions: string[];
25
- };
26
- export type PlanMcpInstallOptions = McpScanOptions & {
27
- env?: Record<string, string | undefined>;
28
- };
29
- export declare function planMcpInstall(capabilityId: string, options?: PlanMcpInstallOptions): Promise<McpInstallPlan>;
@@ -1,109 +0,0 @@
1
- import { scanMcpServers } from './mcp-scan-service.js';
2
- import { findMcpInstallSpec } from './mcp-install-registry.js';
3
- function arraysEqual(a, b) {
4
- if (a.length !== b.length) {
5
- return false;
6
- }
7
- return a.every((value, index) => value === b[index]);
8
- }
9
- function buildEnvCheck(spec, env) {
10
- const missing = spec.envKeys.filter((key) => {
11
- const value = env[key];
12
- return value === undefined || value.length === 0;
13
- });
14
- return { missing };
15
- }
16
- function buildDiff(spec, current) {
17
- const diff = {};
18
- if (current.command !== spec.command) {
19
- diff.command = { before: current.command, after: spec.command };
20
- }
21
- if (!arraysEqual(current.args, spec.args)) {
22
- diff.args = { before: current.args, after: spec.args };
23
- }
24
- if (!arraysEqual(current.envKeys, spec.envKeys)) {
25
- diff.envKeys = { before: current.envKeys, after: spec.envKeys };
26
- }
27
- return Object.keys(diff).length === 0 ? null : diff;
28
- }
29
- function buildNextActions(action, capabilityId, envMissing) {
30
- const actions = [];
31
- if (action === 'unknown-capability') {
32
- actions.push(`No MCP install spec registered for capability ${capabilityId}`);
33
- return actions;
34
- }
35
- if (envMissing.length > 0) {
36
- actions.push(`Set required env vars before apply: ${envMissing.join(', ')}`);
37
- }
38
- if (action === 'add') {
39
- actions.push(`Run peaks mcp apply --capability ${capabilityId} --yes to write the new server`);
40
- }
41
- else if (action === 'update') {
42
- actions.push(`Run peaks mcp apply --capability ${capabilityId} --yes to update the existing peaks-managed server`);
43
- }
44
- else if (action === 'conflict') {
45
- actions.push(`Re-run with --claim to take ownership of the existing non-peaks-managed entry, or rename the conflicting server`);
46
- }
47
- else if (action === 'noop') {
48
- actions.push(`Server already matches the install spec; nothing to apply`);
49
- }
50
- return actions;
51
- }
52
- export async function planMcpInstall(capabilityId, options = {}) {
53
- const spec = findMcpInstallSpec(capabilityId);
54
- if (spec === null) {
55
- return {
56
- capabilityId,
57
- action: 'unknown-capability',
58
- spec: null,
59
- current: null,
60
- envCheck: { missing: [] },
61
- diff: null,
62
- nextActions: buildNextActions('unknown-capability', capabilityId, [])
63
- };
64
- }
65
- const env = options.env ?? process.env;
66
- const envCheck = buildEnvCheck(spec, env);
67
- const scanOptions = {};
68
- if (options.globalSettingsPath !== undefined) {
69
- scanOptions.globalSettingsPath = options.globalSettingsPath;
70
- }
71
- if (options.projectRoot !== undefined) {
72
- scanOptions.projectRoot = options.projectRoot;
73
- }
74
- if (options.managedMarkerPath !== undefined) {
75
- scanOptions.managedMarkerPath = options.managedMarkerPath;
76
- }
77
- if (options.pluginsRegistryPath !== undefined) {
78
- scanOptions.pluginsRegistryPath = options.pluginsRegistryPath;
79
- }
80
- const report = await scanMcpServers(scanOptions);
81
- const current = report.servers.find((server) => server.name === spec.name && server.scope === spec.scope) ?? null;
82
- const pluginDuplicate = report.servers.find((server) => server.source === 'plugin' && server.name === spec.name) ?? null;
83
- let action;
84
- let diff = null;
85
- if (current === null) {
86
- action = 'add';
87
- }
88
- else if (current.source !== 'peaks') {
89
- action = 'conflict';
90
- }
91
- else {
92
- diff = buildDiff(spec, current);
93
- action = diff === null ? 'noop' : 'update';
94
- }
95
- const nextActions = buildNextActions(action, capabilityId, envCheck.missing);
96
- if (pluginDuplicate !== null) {
97
- const pluginLabel = pluginDuplicate.pluginName ?? 'a Claude Code plugin';
98
- nextActions.push(`An MCP server named "${spec.name}" is already loaded by plugin ${pluginLabel}; installing via peaks would duplicate it`);
99
- }
100
- return {
101
- capabilityId,
102
- action,
103
- spec,
104
- current,
105
- envCheck,
106
- diff,
107
- nextActions
108
- };
109
- }
@@ -1,24 +0,0 @@
1
- export type McpJsonRpcRequest = {
2
- jsonrpc: '2.0';
3
- id: number;
4
- method: string;
5
- params?: unknown;
6
- };
7
- export type McpJsonRpcError = {
8
- code: number;
9
- message: string;
10
- data?: unknown;
11
- };
12
- export type McpJsonRpcResponse = {
13
- jsonrpc: '2.0';
14
- id: number;
15
- result?: unknown;
16
- error?: McpJsonRpcError;
17
- };
18
- export type ParsedMessages = {
19
- messages: McpJsonRpcResponse[];
20
- remainder: string;
21
- };
22
- export declare function buildRequest(id: number, method: string, params?: unknown): McpJsonRpcRequest;
23
- export declare function serializeMessage(message: McpJsonRpcRequest | McpJsonRpcResponse): string;
24
- export declare function parseMessages(buffer: string): ParsedMessages;
@@ -1,41 +0,0 @@
1
- export function buildRequest(id, method, params) {
2
- if (params === undefined) {
3
- return { jsonrpc: '2.0', id, method };
4
- }
5
- return { jsonrpc: '2.0', id, method, params };
6
- }
7
- export function serializeMessage(message) {
8
- return `${JSON.stringify(message)}\n`;
9
- }
10
- function isJsonRpcResponse(value) {
11
- if (value === null || typeof value !== 'object' || Array.isArray(value)) {
12
- return false;
13
- }
14
- const record = value;
15
- if (record.jsonrpc !== '2.0') {
16
- return false;
17
- }
18
- return typeof record.id === 'number';
19
- }
20
- export function parseMessages(buffer) {
21
- const lastNewline = buffer.lastIndexOf('\n');
22
- const remainder = lastNewline === -1 ? buffer : buffer.slice(lastNewline + 1);
23
- const completePart = lastNewline === -1 ? '' : buffer.slice(0, lastNewline);
24
- const messages = [];
25
- for (const line of completePart.split('\n')) {
26
- if (line.length === 0) {
27
- continue;
28
- }
29
- let parsed;
30
- try {
31
- parsed = JSON.parse(line);
32
- }
33
- catch {
34
- continue;
35
- }
36
- if (isJsonRpcResponse(parsed)) {
37
- messages.push(parsed);
38
- }
39
- }
40
- return { messages, remainder };
41
- }
@@ -1,8 +0,0 @@
1
- import type { McpScanReport } from './mcp-types.js';
2
- export type McpScanOptions = {
3
- globalSettingsPath?: string;
4
- projectRoot?: string;
5
- managedMarkerPath?: string;
6
- pluginsRegistryPath?: string;
7
- };
8
- export declare function scanMcpServers(options?: McpScanOptions): Promise<McpScanReport>;