peaks-cli 1.0.12 → 1.0.14
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/bin/peaks.js +0 -0
- package/dist/src/cli/commands/config-commands.js +1 -17
- package/dist/src/cli/commands/core-artifact-commands.js +48 -0
- package/dist/src/cli/commands/mcp-commands.d.ts +3 -0
- package/dist/src/cli/commands/mcp-commands.js +144 -0
- package/dist/src/cli/commands/openspec-commands.d.ts +3 -0
- package/dist/src/cli/commands/openspec-commands.js +169 -0
- package/dist/src/cli/commands/project-commands.d.ts +3 -0
- package/dist/src/cli/commands/project-commands.js +37 -0
- package/dist/src/cli/commands/request-commands.d.ts +3 -0
- package/dist/src/cli/commands/request-commands.js +140 -0
- package/dist/src/cli/commands/understand-commands.d.ts +3 -0
- package/dist/src/cli/commands/understand-commands.js +78 -0
- package/dist/src/cli/commands/workflow-commands.js +56 -94
- package/dist/src/cli/program.js +10 -0
- package/dist/src/services/artifacts/request-artifact-service.d.ts +58 -0
- package/dist/src/services/artifacts/request-artifact-service.js +432 -0
- package/dist/src/services/codegraph/codegraph-process-runner.d.ts +2 -0
- package/dist/src/services/codegraph/codegraph-process-runner.js +93 -0
- package/dist/src/services/codegraph/codegraph-service.js +13 -128
- package/dist/src/services/config/config-service.js +2 -22
- package/dist/src/services/dashboard/project-dashboard-service.d.ts +64 -0
- package/dist/src/services/dashboard/project-dashboard-service.js +112 -0
- package/dist/src/services/doctor/doctor-service.d.ts +7 -0
- package/dist/src/services/doctor/doctor-service.js +139 -0
- package/dist/src/services/mcp/mcp-apply-service.d.ts +31 -0
- package/dist/src/services/mcp/mcp-apply-service.js +112 -0
- package/dist/src/services/mcp/mcp-call-service.d.ts +17 -0
- package/dist/src/services/mcp/mcp-call-service.js +34 -0
- package/dist/src/services/mcp/mcp-client-service.d.ts +14 -0
- package/dist/src/services/mcp/mcp-client-service.js +49 -0
- package/dist/src/services/mcp/mcp-install-registry.d.ts +11 -0
- package/dist/src/services/mcp/mcp-install-registry.js +38 -0
- package/dist/src/services/mcp/mcp-plan-service.d.ts +29 -0
- package/dist/src/services/mcp/mcp-plan-service.js +109 -0
- package/dist/src/services/mcp/mcp-protocol.d.ts +24 -0
- package/dist/src/services/mcp/mcp-protocol.js +41 -0
- package/dist/src/services/mcp/mcp-scan-service.d.ts +8 -0
- package/dist/src/services/mcp/mcp-scan-service.js +214 -0
- package/dist/src/services/mcp/mcp-stdio-transport.d.ts +10 -0
- package/dist/src/services/mcp/mcp-stdio-transport.js +50 -0
- package/dist/src/services/mcp/mcp-types.d.ts +31 -0
- package/dist/src/services/mcp/mcp-types.js +1 -0
- package/dist/src/services/openspec/openspec-archive-service.d.ts +12 -0
- package/dist/src/services/openspec/openspec-archive-service.js +28 -0
- package/dist/src/services/openspec/openspec-bridge-service.d.ts +16 -0
- package/dist/src/services/openspec/openspec-bridge-service.js +76 -0
- package/dist/src/services/openspec/openspec-render-service.d.ts +38 -0
- package/dist/src/services/openspec/openspec-render-service.js +130 -0
- package/dist/src/services/openspec/openspec-scan-service.d.ts +6 -0
- package/dist/src/services/openspec/openspec-scan-service.js +123 -0
- package/dist/src/services/openspec/openspec-types.d.ts +39 -0
- package/dist/src/services/openspec/openspec-types.js +1 -0
- package/dist/src/services/openspec/openspec-validate-service.d.ts +27 -0
- package/dist/src/services/openspec/openspec-validate-service.js +77 -0
- package/dist/src/services/recommendations/capability-seed-items.js +2 -1
- package/dist/src/services/recommendations/capability-seed-mappings.js +1 -1
- package/dist/src/services/recommendations/capability-seed-sources.js +1 -1
- package/dist/src/services/shadcn/shadcn-service.d.ts +4 -0
- package/dist/src/services/shadcn/shadcn-service.js +15 -30
- package/dist/src/services/skills/skill-presence-service.d.ts +10 -0
- package/dist/src/services/skills/skill-presence-service.js +54 -0
- package/dist/src/services/skills/skill-runbook-service.d.ts +11 -0
- package/dist/src/services/skills/skill-runbook-service.js +60 -0
- package/dist/src/services/standards/project-standards-service.js +4 -9
- package/dist/src/services/understand/understand-scan-service.d.ts +28 -0
- package/dist/src/services/understand/understand-scan-service.js +157 -0
- package/dist/src/services/understand/understand-types.d.ts +24 -0
- package/dist/src/services/understand/understand-types.js +1 -0
- package/dist/src/services/workflow/workflow-autonomous-service.js +7 -13
- package/dist/src/shared/json-schema-mini.d.ts +10 -0
- package/dist/src/shared/json-schema-mini.js +113 -0
- package/dist/src/shared/paths.d.ts +1 -1
- package/dist/src/shared/paths.js +9 -1
- package/dist/src/shared/version.d.ts +1 -1
- package/dist/src/shared/version.js +1 -1
- package/package.json +2 -8
- package/schemas/doctor-report.schema.json +34 -0
- package/schemas/mcp-apply-result.schema.json +46 -0
- package/schemas/mcp-install-plan.schema.json +71 -0
- package/schemas/mcp-install-spec.schema.json +29 -0
- package/schemas/mcp-server.schema.json +29 -0
- package/schemas/openspec-change-summary.schema.json +68 -0
- package/schemas/openspec-render-request.schema.json +61 -0
- package/schemas/openspec-validation-result.schema.json +36 -0
- package/skills/peaks-prd/SKILL.md +61 -8
- package/skills/peaks-prd/references/artifact-per-request.md +78 -0
- package/skills/peaks-prd/references/workflow.md +7 -5
- package/skills/peaks-qa/SKILL.md +76 -8
- package/skills/peaks-qa/references/artifact-contracts.md +2 -2
- package/skills/peaks-qa/references/artifact-per-request.md +83 -0
- package/skills/peaks-qa/references/openspec-validation-gate.md +55 -0
- package/skills/peaks-qa/references/regression-gates.md +2 -2
- package/skills/peaks-rd/SKILL.md +98 -9
- package/skills/peaks-rd/references/artifact-contracts.md +2 -2
- package/skills/peaks-rd/references/artifact-per-request.md +90 -0
- package/skills/peaks-rd/references/openspec-mcp-cli.md +65 -0
- package/skills/peaks-rd/references/refactor-workflow.md +2 -2
- package/skills/peaks-sc/SKILL.md +46 -0
- package/skills/peaks-sc/references/openspec-commit-boundaries.md +33 -0
- package/skills/peaks-solo/SKILL.md +92 -9
- package/skills/peaks-solo/references/artifact-contracts.md +2 -2
- package/skills/peaks-solo/references/browser-workflow.md +114 -0
- package/skills/peaks-solo/references/external-skill-invocation.md +70 -0
- package/skills/peaks-solo/references/openspec-mcp-workflow.md +53 -0
- package/skills/peaks-solo/references/refactor-mode.md +2 -2
- package/skills/peaks-solo/references/workflow.md +1 -1
- package/skills/peaks-txt/SKILL.md +44 -0
- package/skills/peaks-ui/SKILL.md +59 -33
- package/skills/peaks-ui/references/artifact-per-request.md +71 -0
- package/skills/peaks-ui/references/workflow.md +8 -11
- package/scripts/strip-internal-exports.mjs +0 -33
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { stat } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { isDirectory, pathExists, readText } from '../../shared/fs.js';
|
|
4
|
+
import { getErrorMessage } from '../../shared/result.js';
|
|
5
|
+
function defaultArtifactDir(projectRoot) {
|
|
6
|
+
return join(projectRoot, '.understand-anything');
|
|
7
|
+
}
|
|
8
|
+
function countArrayField(record, key) {
|
|
9
|
+
const value = record[key];
|
|
10
|
+
return Array.isArray(value) ? value.length : 0;
|
|
11
|
+
}
|
|
12
|
+
async function readFlag(path) {
|
|
13
|
+
return { exists: await pathExists(path), path };
|
|
14
|
+
}
|
|
15
|
+
async function readGraph(graphPath) {
|
|
16
|
+
if (!(await pathExists(graphPath))) {
|
|
17
|
+
return { exists: false, path: graphPath };
|
|
18
|
+
}
|
|
19
|
+
const stats = await stat(graphPath);
|
|
20
|
+
const raw = await readText(graphPath);
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(raw);
|
|
23
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
24
|
+
return {
|
|
25
|
+
exists: true,
|
|
26
|
+
path: graphPath,
|
|
27
|
+
sizeBytes: stats.size,
|
|
28
|
+
parseError: 'knowledge-graph.json must be a JSON object'
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const record = parsed;
|
|
32
|
+
return {
|
|
33
|
+
exists: true,
|
|
34
|
+
path: graphPath,
|
|
35
|
+
sizeBytes: stats.size,
|
|
36
|
+
topLevelFields: Object.keys(record).sort(),
|
|
37
|
+
counts: {
|
|
38
|
+
nodes: countArrayField(record, 'nodes'),
|
|
39
|
+
edges: countArrayField(record, 'edges'),
|
|
40
|
+
layers: countArrayField(record, 'layers'),
|
|
41
|
+
tours: countArrayField(record, 'tours')
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
return {
|
|
47
|
+
exists: true,
|
|
48
|
+
path: graphPath,
|
|
49
|
+
sizeBytes: stats.size,
|
|
50
|
+
parseError: getErrorMessage(error)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export async function scanUnderstandAnything(options) {
|
|
55
|
+
const artifactDir = options.artifactDir ?? defaultArtifactDir(options.projectRoot);
|
|
56
|
+
const exists = await isDirectory(artifactDir);
|
|
57
|
+
if (!exists) {
|
|
58
|
+
return {
|
|
59
|
+
exists: false,
|
|
60
|
+
artifactDir,
|
|
61
|
+
graph: { exists: false, path: join(artifactDir, 'knowledge-graph.json') },
|
|
62
|
+
intermediate: { exists: false, path: join(artifactDir, 'intermediate') },
|
|
63
|
+
diffOverlay: { exists: false, path: join(artifactDir, 'diff-overlay.json') }
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const graph = await readGraph(join(artifactDir, 'knowledge-graph.json'));
|
|
67
|
+
const intermediate = await readFlag(join(artifactDir, 'intermediate'));
|
|
68
|
+
const diffOverlay = await readFlag(join(artifactDir, 'diff-overlay.json'));
|
|
69
|
+
return { exists: true, artifactDir, graph, intermediate, diffOverlay };
|
|
70
|
+
}
|
|
71
|
+
function pickStringId(value) {
|
|
72
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const record = value;
|
|
76
|
+
for (const key of ['id', 'path', 'name', 'label']) {
|
|
77
|
+
const candidate = record[key];
|
|
78
|
+
if (typeof candidate === 'string' && candidate.length > 0) {
|
|
79
|
+
return candidate;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
function pickStringField(record, key) {
|
|
85
|
+
const value = record[key];
|
|
86
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
87
|
+
}
|
|
88
|
+
function pickNameArray(record, key) {
|
|
89
|
+
const value = record[key];
|
|
90
|
+
if (!Array.isArray(value)) {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
return value
|
|
94
|
+
.map((entry) => {
|
|
95
|
+
if (typeof entry === 'string') {
|
|
96
|
+
return entry;
|
|
97
|
+
}
|
|
98
|
+
return pickStringId(entry);
|
|
99
|
+
})
|
|
100
|
+
.filter((entry) => entry !== null);
|
|
101
|
+
}
|
|
102
|
+
export async function summarizeKnowledgeGraph(options) {
|
|
103
|
+
const scanOptions = { projectRoot: options.projectRoot };
|
|
104
|
+
if (options.artifactDir !== undefined) {
|
|
105
|
+
scanOptions.artifactDir = options.artifactDir;
|
|
106
|
+
}
|
|
107
|
+
const scan = await scanUnderstandAnything(scanOptions);
|
|
108
|
+
const sampleSize = options.sampleSize ?? 5;
|
|
109
|
+
if (!scan.graph.exists) {
|
|
110
|
+
return {
|
|
111
|
+
exists: false,
|
|
112
|
+
path: scan.graph.path,
|
|
113
|
+
generatedAt: null,
|
|
114
|
+
topLevelFields: [],
|
|
115
|
+
counts: { nodes: 0, edges: 0, layers: 0, tours: 0 },
|
|
116
|
+
layerNames: [],
|
|
117
|
+
tourNames: [],
|
|
118
|
+
sampleNodes: []
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (scan.graph.parseError !== undefined) {
|
|
122
|
+
return {
|
|
123
|
+
exists: true,
|
|
124
|
+
path: scan.graph.path,
|
|
125
|
+
generatedAt: null,
|
|
126
|
+
topLevelFields: [],
|
|
127
|
+
counts: { nodes: 0, edges: 0, layers: 0, tours: 0 },
|
|
128
|
+
layerNames: [],
|
|
129
|
+
tourNames: [],
|
|
130
|
+
sampleNodes: [],
|
|
131
|
+
parseError: scan.graph.parseError
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const raw = await readText(scan.graph.path);
|
|
135
|
+
const parsed = JSON.parse(raw);
|
|
136
|
+
const record = parsed;
|
|
137
|
+
const generatedAt = pickStringField(record, 'generatedAt');
|
|
138
|
+
const nodes = Array.isArray(record.nodes) ? record.nodes : [];
|
|
139
|
+
const sampleNodes = nodes.slice(0, sampleSize)
|
|
140
|
+
.map((entry) => pickStringId(entry))
|
|
141
|
+
.filter((entry) => entry !== null);
|
|
142
|
+
return {
|
|
143
|
+
exists: true,
|
|
144
|
+
path: scan.graph.path,
|
|
145
|
+
generatedAt,
|
|
146
|
+
topLevelFields: Object.keys(record).sort(),
|
|
147
|
+
counts: {
|
|
148
|
+
nodes: countArrayField(record, 'nodes'),
|
|
149
|
+
edges: countArrayField(record, 'edges'),
|
|
150
|
+
layers: countArrayField(record, 'layers'),
|
|
151
|
+
tours: countArrayField(record, 'tours')
|
|
152
|
+
},
|
|
153
|
+
layerNames: pickNameArray(record, 'layers'),
|
|
154
|
+
tourNames: pickNameArray(record, 'tours'),
|
|
155
|
+
sampleNodes
|
|
156
|
+
};
|
|
157
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type UnderstandGraphReport = {
|
|
2
|
+
exists: boolean;
|
|
3
|
+
path: string;
|
|
4
|
+
sizeBytes?: number;
|
|
5
|
+
topLevelFields?: string[];
|
|
6
|
+
counts?: {
|
|
7
|
+
nodes: number;
|
|
8
|
+
edges: number;
|
|
9
|
+
layers: number;
|
|
10
|
+
tours: number;
|
|
11
|
+
};
|
|
12
|
+
parseError?: string;
|
|
13
|
+
};
|
|
14
|
+
export type UnderstandFlagReport = {
|
|
15
|
+
exists: boolean;
|
|
16
|
+
path: string;
|
|
17
|
+
};
|
|
18
|
+
export type UnderstandScanReport = {
|
|
19
|
+
exists: boolean;
|
|
20
|
+
artifactDir: string;
|
|
21
|
+
graph: UnderstandGraphReport;
|
|
22
|
+
intermediate: UnderstandFlagReport;
|
|
23
|
+
diffOverlay: UnderstandFlagReport;
|
|
24
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -250,9 +250,6 @@ function readResumeArtifact(artifactWorkspacePath, artifact) {
|
|
|
250
250
|
return null;
|
|
251
251
|
}
|
|
252
252
|
const pathSegments = artifact.replace(/\\/g, '/').split('/');
|
|
253
|
-
if (pathSegments.length < 4 || pathSegments[0] !== '.peaks') {
|
|
254
|
-
return null;
|
|
255
|
-
}
|
|
256
253
|
const sessionRootPath = resolve(artifactWorkspacePath, '.peaks', pathSegments[1]);
|
|
257
254
|
const roleRootPath = resolve(sessionRootPath, pathSegments[2]);
|
|
258
255
|
if (lstatSync(sessionRootPath).isSymbolicLink() || lstatSync(roleRootPath).isSymbolicLink()) {
|
|
@@ -261,16 +258,13 @@ function readResumeArtifact(artifactWorkspacePath, artifact) {
|
|
|
261
258
|
let allowedRootRealPath;
|
|
262
259
|
if (pathSegments[2] === 'rd') {
|
|
263
260
|
const swarmRootPath = resolve(roleRootPath, 'swarm');
|
|
264
|
-
if (
|
|
261
|
+
if (lstatSync(swarmRootPath).isSymbolicLink()) {
|
|
265
262
|
return null;
|
|
266
263
|
}
|
|
267
264
|
allowedRootRealPath = realpathSync(swarmRootPath);
|
|
268
265
|
}
|
|
269
|
-
else if (pathSegments[2] === 'prd') {
|
|
270
|
-
allowedRootRealPath = realpathSync(roleRootPath);
|
|
271
|
-
}
|
|
272
266
|
else {
|
|
273
|
-
|
|
267
|
+
allowedRootRealPath = realpathSync(roleRootPath);
|
|
274
268
|
}
|
|
275
269
|
const artifactRealPath = realpathSync(artifactPath);
|
|
276
270
|
if (!isInsidePath(allowedRootRealPath, artifactWorkspaceRealPath) || !isInsidePath(artifactRealPath, allowedRootRealPath)) {
|
|
@@ -354,12 +348,12 @@ function parseFrontMatter(content) {
|
|
|
354
348
|
if (lines[0] !== '---') {
|
|
355
349
|
return null;
|
|
356
350
|
}
|
|
357
|
-
const
|
|
358
|
-
if (
|
|
351
|
+
const closingDelimiterIndex = lines.slice(1).findIndex((line) => line === '---');
|
|
352
|
+
if (closingDelimiterIndex === -1) {
|
|
359
353
|
return null;
|
|
360
354
|
}
|
|
361
355
|
const metadata = new Map();
|
|
362
|
-
for (const line of lines.slice(1,
|
|
356
|
+
for (const line of lines.slice(1, closingDelimiterIndex + 1)) {
|
|
363
357
|
const separatorIndex = line.indexOf(':');
|
|
364
358
|
if (separatorIndex === -1) {
|
|
365
359
|
return null;
|
|
@@ -370,8 +364,8 @@ function parseFrontMatter(content) {
|
|
|
370
364
|
}
|
|
371
365
|
function getMarkdownBody(content) {
|
|
372
366
|
const lines = content.split(/\r?\n/);
|
|
373
|
-
const
|
|
374
|
-
return
|
|
367
|
+
const closingDelimiterIndex = lines.slice(1).findIndex((line) => line === '---');
|
|
368
|
+
return closingDelimiterIndex === -1 ? '' : lines.slice(closingDelimiterIndex + 2).join('\n');
|
|
375
369
|
}
|
|
376
370
|
function hasValidationReportBody(body) {
|
|
377
371
|
return body.includes('Validation summary:')
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type JsonSchemaIssue = {
|
|
2
|
+
path: string;
|
|
3
|
+
message: string;
|
|
4
|
+
};
|
|
5
|
+
export type JsonSchemaResult = {
|
|
6
|
+
valid: boolean;
|
|
7
|
+
errors: JsonSchemaIssue[];
|
|
8
|
+
};
|
|
9
|
+
export type JsonSchemaNode = Record<string, unknown>;
|
|
10
|
+
export declare function validateAgainstSchema(value: unknown, schema: JsonSchemaNode): JsonSchemaResult;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
function joinPath(parent, segment) {
|
|
2
|
+
if (parent === '/') {
|
|
3
|
+
return `/${segment}`;
|
|
4
|
+
}
|
|
5
|
+
return `${parent}/${segment}`;
|
|
6
|
+
}
|
|
7
|
+
function typeOf(value) {
|
|
8
|
+
if (value === null) {
|
|
9
|
+
return 'null';
|
|
10
|
+
}
|
|
11
|
+
if (Array.isArray(value)) {
|
|
12
|
+
return 'array';
|
|
13
|
+
}
|
|
14
|
+
if (Number.isInteger(value)) {
|
|
15
|
+
return 'integer';
|
|
16
|
+
}
|
|
17
|
+
return typeof value;
|
|
18
|
+
}
|
|
19
|
+
function matchesType(value, expected) {
|
|
20
|
+
if (expected === 'integer') {
|
|
21
|
+
return typeof value === 'number' && Number.isFinite(value) && Number.isInteger(value);
|
|
22
|
+
}
|
|
23
|
+
if (expected === 'number') {
|
|
24
|
+
return typeof value === 'number' && Number.isFinite(value);
|
|
25
|
+
}
|
|
26
|
+
if (expected === 'array') {
|
|
27
|
+
return Array.isArray(value);
|
|
28
|
+
}
|
|
29
|
+
if (expected === 'null') {
|
|
30
|
+
return value === null;
|
|
31
|
+
}
|
|
32
|
+
if (expected === 'object') {
|
|
33
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
34
|
+
}
|
|
35
|
+
return typeof value === expected;
|
|
36
|
+
}
|
|
37
|
+
function validateNode(value, schema, path, errors) {
|
|
38
|
+
if (Array.isArray(schema.oneOf)) {
|
|
39
|
+
const branches = schema.oneOf;
|
|
40
|
+
const branchErrors = [];
|
|
41
|
+
let matched = false;
|
|
42
|
+
for (const branch of branches) {
|
|
43
|
+
const sink = [];
|
|
44
|
+
validateNode(value, branch, path, sink);
|
|
45
|
+
if (sink.length === 0) {
|
|
46
|
+
matched = true;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
branchErrors.push(sink);
|
|
50
|
+
}
|
|
51
|
+
if (!matched) {
|
|
52
|
+
const detail = branchErrors.map((sink) => sink.map((issue) => issue.message).join(' / ')).join(' | ');
|
|
53
|
+
errors.push({ path, message: `does not match any oneOf branch (${detail})` });
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const expectedType = typeof schema.type === 'string' ? schema.type : null;
|
|
58
|
+
if (expectedType !== null && !matchesType(value, expectedType)) {
|
|
59
|
+
errors.push({ path, message: `expected type ${expectedType}, got ${typeOf(value)}` });
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (typeof value === 'string') {
|
|
63
|
+
if (typeof schema.minLength === 'number' && value.length < schema.minLength) {
|
|
64
|
+
errors.push({ path, message: `string shorter than minLength ${schema.minLength}` });
|
|
65
|
+
}
|
|
66
|
+
if (typeof schema.pattern === 'string' && !new RegExp(schema.pattern).test(value)) {
|
|
67
|
+
errors.push({ path, message: `string does not match pattern ${schema.pattern}` });
|
|
68
|
+
}
|
|
69
|
+
if (Array.isArray(schema.enum) && !schema.enum.includes(value)) {
|
|
70
|
+
errors.push({ path, message: `value ${JSON.stringify(value)} is not in enum` });
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (typeof value === 'number') {
|
|
75
|
+
if (typeof schema.minimum === 'number' && value < schema.minimum) {
|
|
76
|
+
errors.push({ path, message: `number below minimum ${schema.minimum}` });
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (Array.isArray(value)) {
|
|
81
|
+
if (schema.items !== undefined) {
|
|
82
|
+
const itemSchema = schema.items;
|
|
83
|
+
value.forEach((entry, index) => {
|
|
84
|
+
validateNode(entry, itemSchema, joinPath(path, index), errors);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (value !== null && typeof value === 'object') {
|
|
90
|
+
const record = value;
|
|
91
|
+
if (Array.isArray(schema.required)) {
|
|
92
|
+
for (const key of schema.required) {
|
|
93
|
+
if (!Object.prototype.hasOwnProperty.call(record, key)) {
|
|
94
|
+
errors.push({ path, message: `missing required property: ${key}` });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const properties = schema.properties;
|
|
99
|
+
if (properties !== null && typeof properties === 'object' && !Array.isArray(properties)) {
|
|
100
|
+
const propertyMap = properties;
|
|
101
|
+
for (const [key, child] of Object.entries(propertyMap)) {
|
|
102
|
+
if (Object.prototype.hasOwnProperty.call(record, key)) {
|
|
103
|
+
validateNode(record[key], child, joinPath(path, key), errors);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export function validateAgainstSchema(value, schema) {
|
|
110
|
+
const errors = [];
|
|
111
|
+
validateNode(value, schema, '/', errors);
|
|
112
|
+
return { valid: errors.length === 0, errors };
|
|
113
|
+
}
|
|
@@ -3,4 +3,4 @@ export declare const skillsDir: string;
|
|
|
3
3
|
export declare const schemasDir: string;
|
|
4
4
|
export declare const templatesDir: string;
|
|
5
5
|
export declare const requiredSkillNames: readonly ["peaks-solo", "peaks-prd", "peaks-ui", "peaks-rd", "peaks-qa", "peaks-sc", "peaks-txt"];
|
|
6
|
-
export declare const requiredSchemaFiles: readonly ["artifact-manifest.schema.json", "context-capsule.schema.json", "approval-record.schema.json", "change-impact.schema.json", "refactor-slice-spec.schema.json", "artifact-retention-report.schema.json", "capability-source.schema.json", "capability-item.schema.json", "capability-availability.schema.json", "recommendation-plan.schema.json", "artifact-workspace.schema.json"];
|
|
6
|
+
export declare const requiredSchemaFiles: readonly ["artifact-manifest.schema.json", "context-capsule.schema.json", "approval-record.schema.json", "change-impact.schema.json", "refactor-slice-spec.schema.json", "artifact-retention-report.schema.json", "capability-source.schema.json", "capability-item.schema.json", "capability-availability.schema.json", "recommendation-plan.schema.json", "artifact-workspace.schema.json", "mcp-server.schema.json", "mcp-install-spec.schema.json", "mcp-install-plan.schema.json", "mcp-apply-result.schema.json", "openspec-change-summary.schema.json", "openspec-render-request.schema.json", "openspec-validation-result.schema.json", "doctor-report.schema.json"];
|
package/dist/src/shared/paths.js
CHANGED
|
@@ -36,5 +36,13 @@ export const requiredSchemaFiles = [
|
|
|
36
36
|
'capability-item.schema.json',
|
|
37
37
|
'capability-availability.schema.json',
|
|
38
38
|
'recommendation-plan.schema.json',
|
|
39
|
-
'artifact-workspace.schema.json'
|
|
39
|
+
'artifact-workspace.schema.json',
|
|
40
|
+
'mcp-server.schema.json',
|
|
41
|
+
'mcp-install-spec.schema.json',
|
|
42
|
+
'mcp-install-plan.schema.json',
|
|
43
|
+
'mcp-apply-result.schema.json',
|
|
44
|
+
'openspec-change-summary.schema.json',
|
|
45
|
+
'openspec-render-request.schema.json',
|
|
46
|
+
'openspec-validation-result.schema.json',
|
|
47
|
+
'doctor-report.schema.json'
|
|
40
48
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "1.0.
|
|
1
|
+
export declare const CLI_VERSION = "1.0.14";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = "1.0.
|
|
1
|
+
export const CLI_VERSION = "1.0.14";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peaks-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"description": "Peaks CLI and short skill family for Claude Code automation.",
|
|
5
5
|
"author": "SquabbyZ",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
"scripts/sync-version.mjs",
|
|
22
22
|
"scripts/install-skills.mjs",
|
|
23
23
|
"scripts/watch.mjs",
|
|
24
|
-
"scripts/strip-internal-exports.mjs",
|
|
25
24
|
"skills/**",
|
|
26
25
|
"!skills/**/test-prompts.json",
|
|
27
26
|
"!skills/**/.DS_Store",
|
|
@@ -29,14 +28,13 @@
|
|
|
29
28
|
"schemas/*.json"
|
|
30
29
|
],
|
|
31
30
|
"scripts": {
|
|
32
|
-
"build": "node ./scripts/sync-version.mjs && node ./scripts/clean-dist.mjs && tsc -p tsconfig.json
|
|
31
|
+
"build": "node ./scripts/sync-version.mjs && node ./scripts/clean-dist.mjs && tsc -p tsconfig.json",
|
|
33
32
|
"prepack": "npm run build",
|
|
34
33
|
"postinstall": "node ./scripts/install-skills.mjs",
|
|
35
34
|
"dev": "tsx src/cli/index.ts",
|
|
36
35
|
"dev:watch": "node ./scripts/watch.mjs",
|
|
37
36
|
"test": "vitest run",
|
|
38
37
|
"test:coverage": "vitest run --coverage",
|
|
39
|
-
"lint": "eslint eslint.config.js \"bin/**/*.js\" \"scripts/**/*.mjs\" \"src/**/*.ts\" \"tests/**/*.ts\" vitest.config.ts",
|
|
40
38
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
41
39
|
},
|
|
42
40
|
"engines": {
|
|
@@ -48,14 +46,10 @@
|
|
|
48
46
|
"shadcn": "4.7.0"
|
|
49
47
|
},
|
|
50
48
|
"devDependencies": {
|
|
51
|
-
"@eslint/js": "^10.0.1",
|
|
52
49
|
"@types/node": "^22.10.2",
|
|
53
50
|
"@vitest/coverage-v8": "^2.1.8",
|
|
54
|
-
"eslint": "^10.4.0",
|
|
55
|
-
"globals": "^17.6.0",
|
|
56
51
|
"tsx": "^4.19.2",
|
|
57
52
|
"typescript": "^5.7.2",
|
|
58
|
-
"typescript-eslint": "^8.59.4",
|
|
59
53
|
"vitest": "^2.1.8"
|
|
60
54
|
}
|
|
61
55
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Peaks Doctor Report",
|
|
4
|
+
"description": "The envelope emitted by `peaks doctor --json` (data field). Each check.id uses a `<prefix>:<target>` form so callers can route follow-up actions per category.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["checks", "summary"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"checks": {
|
|
9
|
+
"type": "array",
|
|
10
|
+
"items": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"required": ["id", "ok", "message"],
|
|
13
|
+
"properties": {
|
|
14
|
+
"id": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"pattern": "^(skill|skill-name|skill-parse|skill-runbook|skill-apply-note|schema|config|doctor-self|capability):[A-Za-z0-9][A-Za-z0-9._-]*$",
|
|
17
|
+
"description": "Stable check id. Known prefixes: skill:<name> (required skill present), skill-name:<dir> (directory matches declared name), skill-parse:<dir> (skill metadata parsed), skill-runbook:<name> (Default runbook section exists), skill-apply-note:<name> (destructive --apply lines carry an authorization/--dry-run note), schema:<file> (schema file exists and is valid JSON), config:<scope> (optional config locations), doctor-self:<topic> (doctor validates its own output against this schema), capability:<name> (third-party capability is resolvable at the pinned version)."
|
|
18
|
+
},
|
|
19
|
+
"ok": { "type": "boolean" },
|
|
20
|
+
"message": { "type": "string", "minLength": 1 }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"summary": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["ok", "passed", "failed"],
|
|
27
|
+
"properties": {
|
|
28
|
+
"ok": { "type": "boolean" },
|
|
29
|
+
"passed": { "type": "integer", "minimum": 0 },
|
|
30
|
+
"failed": { "type": "integer", "minimum": 0 }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Peaks MCP Apply Result",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": ["capabilityId", "action", "backup", "written", "envCheck"],
|
|
6
|
+
"properties": {
|
|
7
|
+
"capabilityId": { "type": "string", "minLength": 1 },
|
|
8
|
+
"action": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"enum": ["add", "update", "claimed", "noop"],
|
|
11
|
+
"description": "claimed is returned when an existing non-peaks-managed entry was overwritten with --claim."
|
|
12
|
+
},
|
|
13
|
+
"backup": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"required": ["path", "skipped"],
|
|
16
|
+
"properties": {
|
|
17
|
+
"path": {
|
|
18
|
+
"oneOf": [
|
|
19
|
+
{ "type": "null" },
|
|
20
|
+
{ "type": "string", "minLength": 1 }
|
|
21
|
+
],
|
|
22
|
+
"description": "Absolute path to the backup settings.json copy. Null when no prior settings existed or skipped is true."
|
|
23
|
+
},
|
|
24
|
+
"skipped": {
|
|
25
|
+
"type": "boolean",
|
|
26
|
+
"description": "True when action was noop and no write occurred."
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"written": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"required": ["settingsPath", "managedMarkerPath"],
|
|
33
|
+
"properties": {
|
|
34
|
+
"settingsPath": { "type": "string", "minLength": 1 },
|
|
35
|
+
"managedMarkerPath": { "type": "string", "minLength": 1 }
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"envCheck": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"required": ["missing"],
|
|
41
|
+
"properties": {
|
|
42
|
+
"missing": { "type": "array", "items": { "type": "string" } }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Peaks MCP Install Plan",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": ["capabilityId", "action", "spec", "current", "envCheck", "diff", "nextActions"],
|
|
6
|
+
"properties": {
|
|
7
|
+
"capabilityId": { "type": "string", "minLength": 1 },
|
|
8
|
+
"action": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"enum": ["add", "update", "noop", "conflict", "unknown-capability"]
|
|
11
|
+
},
|
|
12
|
+
"spec": {
|
|
13
|
+
"oneOf": [
|
|
14
|
+
{ "type": "null" },
|
|
15
|
+
{ "$ref": "mcp-install-spec.schema.json" }
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"current": {
|
|
19
|
+
"oneOf": [
|
|
20
|
+
{ "type": "null" },
|
|
21
|
+
{ "$ref": "mcp-server.schema.json" }
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"envCheck": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["missing"],
|
|
27
|
+
"properties": {
|
|
28
|
+
"missing": {
|
|
29
|
+
"type": "array",
|
|
30
|
+
"items": { "type": "string" },
|
|
31
|
+
"description": "Env var keys that are not set or are empty."
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"diff": {
|
|
36
|
+
"oneOf": [
|
|
37
|
+
{ "type": "null" },
|
|
38
|
+
{
|
|
39
|
+
"type": "object",
|
|
40
|
+
"properties": {
|
|
41
|
+
"command": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"required": ["before", "after"],
|
|
44
|
+
"properties": {
|
|
45
|
+
"before": { "type": "string" },
|
|
46
|
+
"after": { "type": "string" }
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"args": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"required": ["before", "after"],
|
|
52
|
+
"properties": {
|
|
53
|
+
"before": { "type": "array", "items": { "type": "string" } },
|
|
54
|
+
"after": { "type": "array", "items": { "type": "string" } }
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"envKeys": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"required": ["before", "after"],
|
|
60
|
+
"properties": {
|
|
61
|
+
"before": { "type": "array", "items": { "type": "string" } },
|
|
62
|
+
"after": { "type": "array", "items": { "type": "string" } }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
"nextActions": { "type": "array", "items": { "type": "string" } }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Peaks MCP Install Spec",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": ["capabilityId", "name", "scope", "command", "args", "envKeys"],
|
|
6
|
+
"properties": {
|
|
7
|
+
"capabilityId": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"minLength": 1,
|
|
10
|
+
"description": "Capability id from the Peaks capability map (e.g. context7.docs-lookup)."
|
|
11
|
+
},
|
|
12
|
+
"name": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"minLength": 1,
|
|
15
|
+
"description": "Server key written into .claude/settings.json mcpServers."
|
|
16
|
+
},
|
|
17
|
+
"scope": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"enum": ["global", "project"]
|
|
20
|
+
},
|
|
21
|
+
"command": { "type": "string", "minLength": 1 },
|
|
22
|
+
"args": { "type": "array", "items": { "type": "string" } },
|
|
23
|
+
"envKeys": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"items": { "type": "string" },
|
|
26
|
+
"description": "Required env var names. Values are sourced from process.env at runtime and written as ${VAR} placeholders into settings.json."
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Peaks MCP Server Config",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": ["name", "command", "args", "envKeys", "source", "scope"],
|
|
6
|
+
"properties": {
|
|
7
|
+
"name": { "type": "string", "minLength": 1 },
|
|
8
|
+
"command": { "type": "string", "minLength": 1 },
|
|
9
|
+
"args": { "type": "array", "items": { "type": "string" } },
|
|
10
|
+
"envKeys": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"items": { "type": "string" },
|
|
13
|
+
"description": "Names of env vars referenced by the server config. Values are never stored here."
|
|
14
|
+
},
|
|
15
|
+
"source": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"enum": ["peaks", "cc-switch", "user", "plugin", "unknown"]
|
|
18
|
+
},
|
|
19
|
+
"scope": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"enum": ["global", "project", "plugin"]
|
|
22
|
+
},
|
|
23
|
+
"pluginName": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"minLength": 1,
|
|
26
|
+
"description": "Plugin id (e.g. playwright@claude-plugins-official) when source is plugin."
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|