peaks-cli 1.0.12 → 1.0.13

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 (107) hide show
  1. package/bin/peaks.js +0 -0
  2. package/dist/src/cli/commands/config-commands.js +1 -17
  3. package/dist/src/cli/commands/core-artifact-commands.js +23 -0
  4. package/dist/src/cli/commands/mcp-commands.d.ts +3 -0
  5. package/dist/src/cli/commands/mcp-commands.js +144 -0
  6. package/dist/src/cli/commands/openspec-commands.d.ts +3 -0
  7. package/dist/src/cli/commands/openspec-commands.js +169 -0
  8. package/dist/src/cli/commands/project-commands.d.ts +3 -0
  9. package/dist/src/cli/commands/project-commands.js +37 -0
  10. package/dist/src/cli/commands/request-commands.d.ts +3 -0
  11. package/dist/src/cli/commands/request-commands.js +140 -0
  12. package/dist/src/cli/commands/understand-commands.d.ts +3 -0
  13. package/dist/src/cli/commands/understand-commands.js +78 -0
  14. package/dist/src/cli/commands/workflow-commands.js +56 -94
  15. package/dist/src/cli/program.js +10 -0
  16. package/dist/src/services/artifacts/request-artifact-service.d.ts +58 -0
  17. package/dist/src/services/artifacts/request-artifact-service.js +432 -0
  18. package/dist/src/services/codegraph/codegraph-service.js +26 -45
  19. package/dist/src/services/config/config-service.js +2 -22
  20. package/dist/src/services/dashboard/project-dashboard-service.d.ts +64 -0
  21. package/dist/src/services/dashboard/project-dashboard-service.js +112 -0
  22. package/dist/src/services/doctor/doctor-service.d.ts +7 -0
  23. package/dist/src/services/doctor/doctor-service.js +139 -0
  24. package/dist/src/services/mcp/mcp-apply-service.d.ts +31 -0
  25. package/dist/src/services/mcp/mcp-apply-service.js +112 -0
  26. package/dist/src/services/mcp/mcp-call-service.d.ts +17 -0
  27. package/dist/src/services/mcp/mcp-call-service.js +34 -0
  28. package/dist/src/services/mcp/mcp-client-service.d.ts +14 -0
  29. package/dist/src/services/mcp/mcp-client-service.js +49 -0
  30. package/dist/src/services/mcp/mcp-install-registry.d.ts +11 -0
  31. package/dist/src/services/mcp/mcp-install-registry.js +38 -0
  32. package/dist/src/services/mcp/mcp-plan-service.d.ts +29 -0
  33. package/dist/src/services/mcp/mcp-plan-service.js +109 -0
  34. package/dist/src/services/mcp/mcp-protocol.d.ts +24 -0
  35. package/dist/src/services/mcp/mcp-protocol.js +41 -0
  36. package/dist/src/services/mcp/mcp-scan-service.d.ts +8 -0
  37. package/dist/src/services/mcp/mcp-scan-service.js +214 -0
  38. package/dist/src/services/mcp/mcp-stdio-transport.d.ts +10 -0
  39. package/dist/src/services/mcp/mcp-stdio-transport.js +50 -0
  40. package/dist/src/services/mcp/mcp-types.d.ts +31 -0
  41. package/dist/src/services/mcp/mcp-types.js +1 -0
  42. package/dist/src/services/openspec/openspec-archive-service.d.ts +12 -0
  43. package/dist/src/services/openspec/openspec-archive-service.js +28 -0
  44. package/dist/src/services/openspec/openspec-bridge-service.d.ts +16 -0
  45. package/dist/src/services/openspec/openspec-bridge-service.js +76 -0
  46. package/dist/src/services/openspec/openspec-render-service.d.ts +38 -0
  47. package/dist/src/services/openspec/openspec-render-service.js +130 -0
  48. package/dist/src/services/openspec/openspec-scan-service.d.ts +6 -0
  49. package/dist/src/services/openspec/openspec-scan-service.js +123 -0
  50. package/dist/src/services/openspec/openspec-types.d.ts +39 -0
  51. package/dist/src/services/openspec/openspec-types.js +1 -0
  52. package/dist/src/services/openspec/openspec-validate-service.d.ts +27 -0
  53. package/dist/src/services/openspec/openspec-validate-service.js +77 -0
  54. package/dist/src/services/recommendations/capability-seed-items.js +2 -1
  55. package/dist/src/services/recommendations/capability-seed-mappings.js +1 -1
  56. package/dist/src/services/recommendations/capability-seed-sources.js +1 -1
  57. package/dist/src/services/shadcn/shadcn-service.d.ts +4 -0
  58. package/dist/src/services/shadcn/shadcn-service.js +15 -30
  59. package/dist/src/services/skills/skill-runbook-service.d.ts +11 -0
  60. package/dist/src/services/skills/skill-runbook-service.js +60 -0
  61. package/dist/src/services/standards/project-standards-service.js +4 -9
  62. package/dist/src/services/understand/understand-scan-service.d.ts +28 -0
  63. package/dist/src/services/understand/understand-scan-service.js +157 -0
  64. package/dist/src/services/understand/understand-types.d.ts +24 -0
  65. package/dist/src/services/understand/understand-types.js +1 -0
  66. package/dist/src/shared/json-schema-mini.d.ts +10 -0
  67. package/dist/src/shared/json-schema-mini.js +113 -0
  68. package/dist/src/shared/paths.d.ts +1 -1
  69. package/dist/src/shared/paths.js +9 -1
  70. package/dist/src/shared/version.d.ts +1 -1
  71. package/dist/src/shared/version.js +1 -1
  72. package/package.json +2 -8
  73. package/schemas/doctor-report.schema.json +34 -0
  74. package/schemas/mcp-apply-result.schema.json +46 -0
  75. package/schemas/mcp-install-plan.schema.json +71 -0
  76. package/schemas/mcp-install-spec.schema.json +29 -0
  77. package/schemas/mcp-server.schema.json +29 -0
  78. package/schemas/openspec-change-summary.schema.json +68 -0
  79. package/schemas/openspec-render-request.schema.json +61 -0
  80. package/schemas/openspec-validation-result.schema.json +36 -0
  81. package/skills/peaks-prd/SKILL.md +59 -8
  82. package/skills/peaks-prd/references/artifact-per-request.md +78 -0
  83. package/skills/peaks-prd/references/workflow.md +7 -5
  84. package/skills/peaks-qa/SKILL.md +74 -8
  85. package/skills/peaks-qa/references/artifact-contracts.md +2 -2
  86. package/skills/peaks-qa/references/artifact-per-request.md +83 -0
  87. package/skills/peaks-qa/references/openspec-validation-gate.md +55 -0
  88. package/skills/peaks-qa/references/regression-gates.md +2 -2
  89. package/skills/peaks-rd/SKILL.md +96 -9
  90. package/skills/peaks-rd/references/artifact-contracts.md +2 -2
  91. package/skills/peaks-rd/references/artifact-per-request.md +90 -0
  92. package/skills/peaks-rd/references/openspec-mcp-cli.md +65 -0
  93. package/skills/peaks-rd/references/refactor-workflow.md +2 -2
  94. package/skills/peaks-sc/SKILL.md +44 -0
  95. package/skills/peaks-sc/references/openspec-commit-boundaries.md +33 -0
  96. package/skills/peaks-solo/SKILL.md +90 -9
  97. package/skills/peaks-solo/references/artifact-contracts.md +2 -2
  98. package/skills/peaks-solo/references/browser-workflow.md +114 -0
  99. package/skills/peaks-solo/references/external-skill-invocation.md +70 -0
  100. package/skills/peaks-solo/references/openspec-mcp-workflow.md +53 -0
  101. package/skills/peaks-solo/references/refactor-mode.md +2 -2
  102. package/skills/peaks-solo/references/workflow.md +1 -1
  103. package/skills/peaks-txt/SKILL.md +42 -0
  104. package/skills/peaks-ui/SKILL.md +57 -33
  105. package/skills/peaks-ui/references/artifact-per-request.md +71 -0
  106. package/skills/peaks-ui/references/workflow.md +8 -11
  107. package/scripts/strip-internal-exports.mjs +0 -33
@@ -0,0 +1,60 @@
1
+ import { readText } from '../../shared/fs.js';
2
+ import { loadSkillRegistry } from './skill-registry.js';
3
+ const DESTRUCTIVE_APPLY_PATTERNS = [
4
+ /peaks\s+memory\s+sync[^\n]*--apply/,
5
+ /peaks\s+memory\s+extract[^\n]*--apply/,
6
+ /peaks\s+artifacts\s+sync[^\n]*--apply/,
7
+ /peaks\s+openspec\s+archive[^\n]*--apply/,
8
+ /peaks\s+standards\s+(?:init|update)[^\n]*--apply/
9
+ ];
10
+ const AUTHORIZATION_KEYWORDS_PATTERN = /authoriz|explicit|--dry-run|approv|only after|only when/i;
11
+ const PEAKS_COMMAND_LINE_PATTERN = /^\s*peaks\s+\w/;
12
+ function extractRunbookSection(body) {
13
+ const match = /## Default runbook\n+([\s\S]*?)(?=\n## |$)/.exec(body);
14
+ return match === null ? null : (match[1] ?? null);
15
+ }
16
+ function findDestructiveApplyLines(section) {
17
+ const lines = section.split(/\r?\n/);
18
+ return lines.filter((line) => DESTRUCTIVE_APPLY_PATTERNS.some((pattern) => pattern.test(line)));
19
+ }
20
+ function findPeaksCommandLines(section) {
21
+ return section
22
+ .split(/\r?\n/)
23
+ .filter((line) => PEAKS_COMMAND_LINE_PATTERN.test(line))
24
+ .map((line) => line.trim());
25
+ }
26
+ export async function inspectSkillRunbook(name, baseDir) {
27
+ const registry = await loadSkillRegistry(baseDir);
28
+ const skill = registry.skills.find((entry) => entry.name === name);
29
+ if (skill === undefined) {
30
+ throw new Error(`Skill "${name}" not found under skills directory`);
31
+ }
32
+ const body = await readText(skill.skillPath);
33
+ const section = extractRunbookSection(body);
34
+ if (section === null) {
35
+ return {
36
+ name: skill.name,
37
+ directory: skill.directory,
38
+ hasRunbook: false,
39
+ peaksCommandCount: 0,
40
+ peaksCommandLines: [],
41
+ destructiveApplyLines: [],
42
+ hasAuthorizationNote: false,
43
+ ok: false
44
+ };
45
+ }
46
+ const peaksCommandLines = findPeaksCommandLines(section);
47
+ const destructiveApplyLines = findDestructiveApplyLines(section);
48
+ const hasAuthorizationNote = AUTHORIZATION_KEYWORDS_PATTERN.test(section);
49
+ const ok = destructiveApplyLines.length === 0 || hasAuthorizationNote;
50
+ return {
51
+ name: skill.name,
52
+ directory: skill.directory,
53
+ hasRunbook: true,
54
+ peaksCommandCount: peaksCommandLines.length,
55
+ peaksCommandLines,
56
+ destructiveApplyLines,
57
+ hasAuthorizationNote,
58
+ ok
59
+ };
60
+ }
@@ -96,20 +96,17 @@ function renderClaudeMd(language) {
96
96
  '- peaks-solo summarizes RD and QA standards preflight before end-to-end code workflows.',
97
97
  '',
98
98
  'Rules:',
99
- '- RED LINE: Read `.claude/rules/common/coding-style.md` before editing code, and treat its file-size limits as blockers.',
100
- '- RED LINE: Strictly prohibit oversized single files; split large files into cohesive modules before handoff.',
99
+ '- Read `.claude/rules/common/coding-style.md` before editing code.',
101
100
  '- Read `.claude/rules/common/code-review.md` before reviewing changes.',
102
101
  '- Read `.claude/rules/common/security.md` before touching filesystem, user input, external calls, auth, or secrets.',
103
102
  `- Read .claude/rules/${language}/coding-style.md for language-specific standards when applicable.`,
104
103
  '',
105
- 'External references: https://github.com/affaan-m/everything-claude-code and https://github.com/SquabbyZ/andrej-karpathy-skills are used as curated references only. Treat andrej-karpathy-skills code quality guidance as a red line during peaks-rd implementation. Do not execute or install external content without explicit approval.',
104
+ 'External reference: https://github.com/affaan-m/everything-claude-code is used as a curated reference only. Do not execute or install external content without explicit approval.',
106
105
  ''
107
106
  ].join('\n');
108
107
  }
109
108
  function renderCommonCodingStyle() {
110
- return `${renderHeader('Common Coding Standards')}- RED LINE: Strictly prohibit oversized single files; split large files into cohesive modules before handoff.
111
- - RED LINE: Treat https://github.com/SquabbyZ/andrej-karpathy-skills code quality guidance as a blocker for peaks-rd implementation.
112
- - Prefer simple, readable code over clever abstractions.
109
+ return `${renderHeader('Common Coding Standards')}- Prefer simple, readable code over clever abstractions.
113
110
  - Keep functions focused and files cohesive.
114
111
  - Use immutable updates unless a language-specific convention explicitly favors mutation.
115
112
  - Validate user input, external data, file paths, and configuration at system boundaries.
@@ -117,9 +114,7 @@ function renderCommonCodingStyle() {
117
114
  `;
118
115
  }
119
116
  function renderCodeReview() {
120
- return `${renderHeader('Code Review Standards')}- RED LINE: Block oversized single-file changes and require cohesive module splits before approval.
121
- - RED LINE: Treat https://github.com/SquabbyZ/andrej-karpathy-skills code quality guidance as a blocker during review.
122
- - Review diffs for correctness, maintainability, test coverage, and regression risk.
117
+ return `${renderHeader('Code Review Standards')}- Review diffs for correctness, maintainability, test coverage, and regression risk.
123
118
  - Treat missing tests for changed behavior as a blocker unless the change is documentation-only.
124
119
  - Verify code paths that handle filesystem, external APIs, credentials, user input, or generated artifacts.
125
120
  - peaks-qa must use this guidance as part of code workflow preflight and final verification.
@@ -0,0 +1,28 @@
1
+ import type { UnderstandScanReport } from './understand-types.js';
2
+ export type UnderstandScanOptions = {
3
+ projectRoot: string;
4
+ artifactDir?: string;
5
+ };
6
+ export declare function scanUnderstandAnything(options: UnderstandScanOptions): Promise<UnderstandScanReport>;
7
+ export type UnderstandGraphSummary = {
8
+ exists: boolean;
9
+ path: string;
10
+ generatedAt: string | null;
11
+ topLevelFields: string[];
12
+ counts: {
13
+ nodes: number;
14
+ edges: number;
15
+ layers: number;
16
+ tours: number;
17
+ };
18
+ layerNames: string[];
19
+ tourNames: string[];
20
+ sampleNodes: string[];
21
+ parseError?: string;
22
+ };
23
+ export type SummarizeKnowledgeGraphOptions = {
24
+ projectRoot: string;
25
+ artifactDir?: string;
26
+ sampleSize?: number;
27
+ };
28
+ export declare function summarizeKnowledgeGraph(options: SummarizeKnowledgeGraphOptions): Promise<UnderstandGraphSummary>;
@@ -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 {};
@@ -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"];
@@ -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.12";
1
+ export declare const CLI_VERSION = "1.0.13";
@@ -1 +1 @@
1
- export const CLI_VERSION = "1.0.12";
1
+ export const CLI_VERSION = "1.0.13";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peaks-cli",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
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 && node ./scripts/strip-internal-exports.mjs",
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
+ }