peaks-cli 1.0.13 → 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/dist/src/cli/commands/core-artifact-commands.js +25 -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 +2 -98
- package/dist/src/services/mcp/mcp-scan-service.js +1 -1
- 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.js +1 -1
- package/dist/src/services/workflow/workflow-autonomous-service.js +7 -13
- 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 +2 -0
- package/skills/peaks-qa/SKILL.md +2 -0
- package/skills/peaks-rd/SKILL.md +2 -0
- package/skills/peaks-sc/SKILL.md +2 -0
- package/skills/peaks-solo/SKILL.md +2 -0
- package/skills/peaks-txt/SKILL.md +2 -0
- package/skills/peaks-ui/SKILL.md +2 -0
|
@@ -7,6 +7,7 @@ import { planProxyTest } from '../../services/proxy/proxy-service.js';
|
|
|
7
7
|
import { runDoctor } from '../../services/doctor/doctor-service.js';
|
|
8
8
|
import { listSkills } from '../../services/skills/skill-registry.js';
|
|
9
9
|
import { inspectSkillRunbook } from '../../services/skills/skill-runbook-service.js';
|
|
10
|
+
import { setSkillPresence, clearSkillPresence, getSkillPresence } from '../../services/skills/skill-presence-service.js';
|
|
10
11
|
import { fail, ok } from '../../shared/result.js';
|
|
11
12
|
import { addJsonOption, failUnsupportedNonDryRun, getErrorMessage, isArtifactProvider, isArtifactSetupStep, printResult } from '../cli-helpers.js';
|
|
12
13
|
export function registerCoreAndArtifactCommands(program, io) {
|
|
@@ -56,6 +57,30 @@ export function registerCoreAndArtifactCommands(program, io) {
|
|
|
56
57
|
process.exitCode = 1;
|
|
57
58
|
}
|
|
58
59
|
});
|
|
60
|
+
addJsonOption(skill
|
|
61
|
+
.command('presence')
|
|
62
|
+
.description('Show the currently active Peaks skill')).action((options) => {
|
|
63
|
+
const presence = getSkillPresence();
|
|
64
|
+
if (presence === null) {
|
|
65
|
+
printResult(io, ok('skill.presence', { active: false }), options.json);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
printResult(io, ok('skill.presence', { active: true, ...presence }), options.json);
|
|
69
|
+
});
|
|
70
|
+
addJsonOption(skill
|
|
71
|
+
.command('presence:set <name>')
|
|
72
|
+
.description('Set the currently active Peaks skill for session-wide visibility')
|
|
73
|
+
.option('--mode <mode>', 'execution mode')
|
|
74
|
+
.option('--gate <gate>', 'current gate')).action((name, options) => {
|
|
75
|
+
const presence = setSkillPresence(name, options.mode, options.gate);
|
|
76
|
+
printResult(io, ok('skill.presence:set', { active: true, ...presence }), options.json);
|
|
77
|
+
});
|
|
78
|
+
addJsonOption(skill
|
|
79
|
+
.command('presence:clear')
|
|
80
|
+
.description('Clear the active Peaks skill presence indicator')).action((options) => {
|
|
81
|
+
const removed = clearSkillPresence();
|
|
82
|
+
printResult(io, ok('skill.presence:clear', { active: false, removed }), options.json);
|
|
83
|
+
});
|
|
59
84
|
const profile = program.command('profile').description('Manage runtime profiles');
|
|
60
85
|
addJsonOption(profile.command('list').description('List available profiles')).action((options) => {
|
|
61
86
|
printResult(io, ok('profile.list', { profiles: listProfiles() }), options.json);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const CODEGRAPH_PROCESS_TIMEOUT_MS = 600_000;
|
|
4
|
+
const CODEGRAPH_OUTPUT_LIMIT_BYTES = 10 * 1024 * 1024;
|
|
5
|
+
function createCodegraphEnvironment(sourceEnv = process.env) {
|
|
6
|
+
const preservedKeys = ['PATH', 'Path', 'HOME', 'USERPROFILE', 'APPDATA', 'LOCALAPPDATA', 'TEMP', 'TMP', 'SystemRoot', 'WINDIR'];
|
|
7
|
+
const environment = {};
|
|
8
|
+
for (const key of preservedKeys) {
|
|
9
|
+
const value = sourceEnv[key];
|
|
10
|
+
if (value !== undefined) {
|
|
11
|
+
environment[key] = value;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return environment;
|
|
15
|
+
}
|
|
16
|
+
function assertOutputLimit(currentSize, chunkSize) {
|
|
17
|
+
const nextSize = currentSize + chunkSize;
|
|
18
|
+
if (nextSize > CODEGRAPH_OUTPUT_LIMIT_BYTES) {
|
|
19
|
+
throw new Error(`codegraph output exceeded ${CODEGRAPH_OUTPUT_LIMIT_BYTES} bytes`);
|
|
20
|
+
}
|
|
21
|
+
return nextSize;
|
|
22
|
+
}
|
|
23
|
+
function terminateCodegraphProcess(childProcess) {
|
|
24
|
+
if (childProcess.pid === undefined) {
|
|
25
|
+
childProcess.kill();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (process.platform === 'win32') {
|
|
29
|
+
if (process.env.SystemRoot) {
|
|
30
|
+
spawn(join(process.env.SystemRoot, 'System32', 'taskkill.exe'), ['/pid', String(childProcess.pid), '/T', '/F'], { shell: false, stdio: 'ignore' });
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
childProcess.kill();
|
|
34
|
+
}
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
process.kill(-childProcess.pid, 'SIGTERM');
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
childProcess.kill('SIGTERM');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function defaultCodegraphProcessRunner(invocation) {
|
|
45
|
+
return new Promise((resolveResult, reject) => {
|
|
46
|
+
const childProcess = spawn(invocation.executable, invocation.args, {
|
|
47
|
+
cwd: invocation.cwd,
|
|
48
|
+
detached: process.platform !== 'win32',
|
|
49
|
+
env: createCodegraphEnvironment(),
|
|
50
|
+
shell: false
|
|
51
|
+
});
|
|
52
|
+
const timeout = setTimeout(() => {
|
|
53
|
+
terminateCodegraphProcess(childProcess);
|
|
54
|
+
reject(new Error(`codegraph process timed out after ${CODEGRAPH_PROCESS_TIMEOUT_MS}ms`));
|
|
55
|
+
}, CODEGRAPH_PROCESS_TIMEOUT_MS);
|
|
56
|
+
const stdoutChunks = [];
|
|
57
|
+
const stderrChunks = [];
|
|
58
|
+
let stdoutSize = 0;
|
|
59
|
+
let stderrSize = 0;
|
|
60
|
+
childProcess.stdout.on('data', (chunk) => {
|
|
61
|
+
try {
|
|
62
|
+
stdoutSize = assertOutputLimit(stdoutSize, chunk.length);
|
|
63
|
+
stdoutChunks.push(chunk);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
terminateCodegraphProcess(childProcess);
|
|
67
|
+
reject(error);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
childProcess.stderr.on('data', (chunk) => {
|
|
71
|
+
try {
|
|
72
|
+
stderrSize = assertOutputLimit(stderrSize, chunk.length);
|
|
73
|
+
stderrChunks.push(chunk);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
terminateCodegraphProcess(childProcess);
|
|
77
|
+
reject(error);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
childProcess.on('error', (error) => {
|
|
81
|
+
clearTimeout(timeout);
|
|
82
|
+
reject(error);
|
|
83
|
+
});
|
|
84
|
+
childProcess.on('close', (exitCode) => {
|
|
85
|
+
clearTimeout(timeout);
|
|
86
|
+
resolveResult({
|
|
87
|
+
exitCode,
|
|
88
|
+
stdout: Buffer.concat(stdoutChunks).toString('utf8'),
|
|
89
|
+
stderr: Buffer.concat(stderrChunks).toString('utf8')
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import { existsSync, realpathSync, statSync } from 'node:fs';
|
|
2
|
-
import { spawn } from 'node:child_process';
|
|
3
2
|
import { createRequire } from 'node:module';
|
|
4
|
-
import { dirname, isAbsolute,
|
|
3
|
+
import { dirname, isAbsolute, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import { defaultCodegraphProcessRunner } from './codegraph-process-runner.js';
|
|
5
5
|
const CODEGRAPH_PACKAGE_NAME = '@colbymchenry/codegraph';
|
|
6
6
|
const CODEGRAPH_PACKAGE_VERSION = '0.7.10';
|
|
7
7
|
const CODEGRAPH_EXECUTABLE = process.execPath;
|
|
8
8
|
const CODEGRAPH_BINARY_PATH = resolveCodegraphBinaryPath();
|
|
9
|
-
const CODEGRAPH_PROCESS_TIMEOUT_MS = 600_000;
|
|
10
|
-
const CODEGRAPH_OUTPUT_LIMIT_BYTES = 10 * 1024 * 1024;
|
|
11
|
-
const NODE_OPTIONS_ENV_KEY = 'NODE_OPTIONS';
|
|
12
|
-
const NPM_CONFIG_PREFIX = 'npm_config_';
|
|
13
|
-
const NPM_CONFIG_UPPER_PREFIX = 'NPM_CONFIG_';
|
|
14
9
|
const POSITIONAL_ARGUMENT_PREFIX = '-';
|
|
15
10
|
const ALLOWED_SUBCOMMANDS = ['status', 'init', 'index', 'query', 'files', 'context', 'affected'];
|
|
16
11
|
const NUMERIC_FLAG_NAMES = ['limit', 'maxDepth'];
|
|
@@ -28,9 +23,6 @@ function resolveCodegraphBinaryPath() {
|
|
|
28
23
|
const require = createRequire(import.meta.url);
|
|
29
24
|
const packageJsonPath = require.resolve('@colbymchenry/codegraph/package.json');
|
|
30
25
|
const binaryPath = resolve(dirname(packageJsonPath), 'dist', 'bin', 'codegraph.js');
|
|
31
|
-
if (!existsSync(binaryPath)) {
|
|
32
|
-
throw new Error('Unable to resolve local codegraph binary from @colbymchenry/codegraph');
|
|
33
|
-
}
|
|
34
26
|
return binaryPath;
|
|
35
27
|
}
|
|
36
28
|
function assertSupportedSubcommand(subcommand) {
|
|
@@ -95,9 +87,6 @@ function resolveExistingBoundary(absoluteFilePath) {
|
|
|
95
87
|
let currentPath = dirname(absoluteFilePath);
|
|
96
88
|
while (!existsSync(currentPath)) {
|
|
97
89
|
const parentPath = dirname(currentPath);
|
|
98
|
-
if (parentPath === currentPath) {
|
|
99
|
-
return currentPath;
|
|
100
|
-
}
|
|
101
90
|
currentPath = parentPath;
|
|
102
91
|
}
|
|
103
92
|
return currentPath;
|
|
@@ -150,91 +139,6 @@ function buildCommandArgs(options, projectRoot) {
|
|
|
150
139
|
}
|
|
151
140
|
return args;
|
|
152
141
|
}
|
|
153
|
-
function createCodegraphEnvironment(sourceEnv = process.env) {
|
|
154
|
-
const preservedKeys = ['PATH', 'Path', 'HOME', 'USERPROFILE', 'APPDATA', 'LOCALAPPDATA', 'TEMP', 'TMP', 'SystemRoot', 'WINDIR'];
|
|
155
|
-
const environment = {};
|
|
156
|
-
for (const key of preservedKeys) {
|
|
157
|
-
const value = sourceEnv[key];
|
|
158
|
-
if (value !== undefined) {
|
|
159
|
-
environment[key] = value;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return environment;
|
|
163
|
-
}
|
|
164
|
-
function assertOutputLimit(currentSize, chunkSize) {
|
|
165
|
-
const nextSize = currentSize + chunkSize;
|
|
166
|
-
if (nextSize > CODEGRAPH_OUTPUT_LIMIT_BYTES) {
|
|
167
|
-
throw new Error(`codegraph output exceeded ${CODEGRAPH_OUTPUT_LIMIT_BYTES} bytes`);
|
|
168
|
-
}
|
|
169
|
-
return nextSize;
|
|
170
|
-
}
|
|
171
|
-
function terminateCodegraphProcess(childProcess) {
|
|
172
|
-
if (childProcess.pid === undefined) {
|
|
173
|
-
childProcess.kill();
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
if (process.platform === 'win32') {
|
|
177
|
-
const taskkillPath = process.env.SystemRoot ? join(process.env.SystemRoot, 'System32', 'taskkill.exe') : 'taskkill.exe';
|
|
178
|
-
spawn(taskkillPath, ['/pid', String(childProcess.pid), '/T', '/F'], { shell: false, stdio: 'ignore' });
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
try {
|
|
182
|
-
process.kill(-childProcess.pid, 'SIGTERM');
|
|
183
|
-
}
|
|
184
|
-
catch {
|
|
185
|
-
childProcess.kill('SIGTERM');
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
function defaultCodegraphProcessRunner(invocation) {
|
|
189
|
-
return new Promise((resolveResult, reject) => {
|
|
190
|
-
const childProcess = spawn(invocation.executable, invocation.args, {
|
|
191
|
-
cwd: invocation.cwd,
|
|
192
|
-
detached: process.platform !== 'win32',
|
|
193
|
-
env: createCodegraphEnvironment(),
|
|
194
|
-
shell: false
|
|
195
|
-
});
|
|
196
|
-
const timeout = setTimeout(() => {
|
|
197
|
-
terminateCodegraphProcess(childProcess);
|
|
198
|
-
reject(new Error(`codegraph process timed out after ${CODEGRAPH_PROCESS_TIMEOUT_MS}ms`));
|
|
199
|
-
}, CODEGRAPH_PROCESS_TIMEOUT_MS);
|
|
200
|
-
const stdoutChunks = [];
|
|
201
|
-
const stderrChunks = [];
|
|
202
|
-
let stdoutSize = 0;
|
|
203
|
-
let stderrSize = 0;
|
|
204
|
-
childProcess.stdout.on('data', (chunk) => {
|
|
205
|
-
try {
|
|
206
|
-
stdoutSize = assertOutputLimit(stdoutSize, chunk.length);
|
|
207
|
-
stdoutChunks.push(chunk);
|
|
208
|
-
}
|
|
209
|
-
catch (error) {
|
|
210
|
-
terminateCodegraphProcess(childProcess);
|
|
211
|
-
reject(error);
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
childProcess.stderr.on('data', (chunk) => {
|
|
215
|
-
try {
|
|
216
|
-
stderrSize = assertOutputLimit(stderrSize, chunk.length);
|
|
217
|
-
stderrChunks.push(chunk);
|
|
218
|
-
}
|
|
219
|
-
catch (error) {
|
|
220
|
-
terminateCodegraphProcess(childProcess);
|
|
221
|
-
reject(error);
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
childProcess.on('error', (error) => {
|
|
225
|
-
clearTimeout(timeout);
|
|
226
|
-
reject(error);
|
|
227
|
-
});
|
|
228
|
-
childProcess.on('close', (exitCode) => {
|
|
229
|
-
clearTimeout(timeout);
|
|
230
|
-
resolveResult({
|
|
231
|
-
exitCode,
|
|
232
|
-
stdout: Buffer.concat(stdoutChunks).toString('utf8'),
|
|
233
|
-
stderr: Buffer.concat(stderrChunks).toString('utf8')
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
142
|
export function createCodegraphInvocation(options) {
|
|
239
143
|
assertSupportedSubcommand(options.subcommand);
|
|
240
144
|
const projectRoot = resolveProjectRoot(options.project);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type SkillPresence = {
|
|
2
|
+
skill: string;
|
|
3
|
+
mode?: string;
|
|
4
|
+
gate?: string;
|
|
5
|
+
setAt: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function exportSkillPresence(): string;
|
|
8
|
+
export declare function setSkillPresence(skill: string, mode?: string, gate?: string): SkillPresence;
|
|
9
|
+
export declare function getSkillPresence(): SkillPresence | null;
|
|
10
|
+
export declare function clearSkillPresence(): boolean;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
const PRESENCE_FILE = '.peaks/.active-skill.json';
|
|
4
|
+
function resolvePresencePath() {
|
|
5
|
+
return resolve(process.cwd(), PRESENCE_FILE);
|
|
6
|
+
}
|
|
7
|
+
export function exportSkillPresence() {
|
|
8
|
+
return resolvePresencePath();
|
|
9
|
+
}
|
|
10
|
+
export function setSkillPresence(skill, mode, gate) {
|
|
11
|
+
const presence = {
|
|
12
|
+
skill,
|
|
13
|
+
...(mode ? { mode } : {}),
|
|
14
|
+
...(gate ? { gate } : {}),
|
|
15
|
+
setAt: new Date().toISOString()
|
|
16
|
+
};
|
|
17
|
+
const presencePath = resolvePresencePath();
|
|
18
|
+
const presenceDir = dirname(presencePath);
|
|
19
|
+
if (!existsSync(presenceDir)) {
|
|
20
|
+
mkdirSync(presenceDir, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
writeFileSync(presencePath, JSON.stringify(presence, null, 2), 'utf8');
|
|
23
|
+
return presence;
|
|
24
|
+
}
|
|
25
|
+
export function getSkillPresence() {
|
|
26
|
+
const presencePath = resolvePresencePath();
|
|
27
|
+
if (!existsSync(presencePath)) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const raw = readFileSync(presencePath, 'utf8');
|
|
32
|
+
const parsed = JSON.parse(raw);
|
|
33
|
+
if (typeof parsed?.skill !== 'string' || parsed.skill.length === 0) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return parsed;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function clearSkillPresence() {
|
|
43
|
+
const presencePath = resolvePresencePath();
|
|
44
|
+
if (!existsSync(presencePath)) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
unlinkSync(presencePath);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -11,7 +11,7 @@ const AUTHORIZATION_KEYWORDS_PATTERN = /authoriz|explicit|--dry-run|approv|only
|
|
|
11
11
|
const PEAKS_COMMAND_LINE_PATTERN = /^\s*peaks\s+\w/;
|
|
12
12
|
function extractRunbookSection(body) {
|
|
13
13
|
const match = /## Default runbook\n+([\s\S]*?)(?=\n## |$)/.exec(body);
|
|
14
|
-
return match === null ? null :
|
|
14
|
+
return match === null ? null : match[1];
|
|
15
15
|
}
|
|
16
16
|
function findDestructiveApplyLines(section) {
|
|
17
17
|
const lines = section.split(/\r?\n/);
|
|
@@ -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:')
|
|
@@ -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
|
@@ -34,6 +34,7 @@ For a feature / bug / clarification request with no authenticated source documen
|
|
|
34
34
|
```bash
|
|
35
35
|
# 0. confirm PRD's own runbook integrity before driving any phase
|
|
36
36
|
peaks skill runbook peaks-prd --json
|
|
37
|
+
peaks skill presence:set peaks-prd # show persistent skill presence every turn
|
|
37
38
|
|
|
38
39
|
# 1. capture the request as the canonical PRD artifact (preview, then apply)
|
|
39
40
|
peaks request init --role prd --id <request-id> --project <repo> --json
|
|
@@ -53,6 +54,7 @@ peaks codegraph status --project <repo> # local index status
|
|
|
53
54
|
|
|
54
55
|
# 5. write goals / non-goals / acceptance into the artifact body, then hand off
|
|
55
56
|
peaks request show <request-id> --role prd --project <repo> --json
|
|
57
|
+
peaks skill presence:clear # handoff complete, remove presence indicator
|
|
56
58
|
```
|
|
57
59
|
|
|
58
60
|
For an authenticated product document request (Feishu/Lark/wiki), add before step 5:
|
package/skills/peaks-qa/SKILL.md
CHANGED
|
@@ -33,6 +33,7 @@ The default sequence the QA skill should execute. Do not skip the boundary check
|
|
|
33
33
|
```bash
|
|
34
34
|
# 0. confirm QA's own runbook integrity before validating anything
|
|
35
35
|
peaks skill runbook peaks-qa --json
|
|
36
|
+
peaks skill presence:set peaks-qa # show persistent skill presence every turn
|
|
36
37
|
|
|
37
38
|
# 1. capture the QA request artifact and read upstream scope
|
|
38
39
|
peaks request init --role qa --id <request-id> --project <repo> --apply --json
|
|
@@ -69,6 +70,7 @@ peaks mcp apply --capability playwright-mcp.browser-validation --yes --json
|
|
|
69
70
|
# 7. on verdict=return-to-rd, route findings back through the request id; otherwise close.
|
|
70
71
|
peaks request show <request-id> --role qa --project <repo> --json
|
|
71
72
|
peaks openspec archive <change-id> --project <repo> --json # preview, then --apply on full pass
|
|
73
|
+
peaks skill presence:clear # QA complete, remove presence indicator
|
|
72
74
|
```
|
|
73
75
|
|
|
74
76
|
Verdict `pass` is blocked until every applicable validation gate has evidence in the artifact.
|
package/skills/peaks-rd/SKILL.md
CHANGED
|
@@ -31,6 +31,7 @@ The default sequence the RD skill should execute for a code-touching request. Sk
|
|
|
31
31
|
```bash
|
|
32
32
|
# 0. confirm RD's own runbook integrity before any code edit
|
|
33
33
|
peaks skill runbook peaks-rd --json
|
|
34
|
+
peaks skill presence:set peaks-rd # show persistent skill presence every turn
|
|
34
35
|
|
|
35
36
|
# 1. capture the RD request artifact and read upstream PRD / UI scope
|
|
36
37
|
peaks request init --role rd --id <request-id> --project <repo> --apply --json
|
|
@@ -65,6 +66,7 @@ peaks openspec validate <change-id> --project <repo> --json # exit gate (re-r
|
|
|
65
66
|
# 8. hand off to QA via the cross-linked request id
|
|
66
67
|
peaks request init --role qa --id <request-id> --project <repo> --apply --json
|
|
67
68
|
peaks request show <request-id> --role rd --project <repo> --json
|
|
69
|
+
peaks skill presence:clear # handoff complete, remove presence indicator
|
|
68
70
|
```
|
|
69
71
|
|
|
70
72
|
For refactor work, the coverage ≥ 95% gate in `Refactor hard gates` still applies and must be recorded in the artifact before slicing begins.
|
package/skills/peaks-sc/SKILL.md
CHANGED
|
@@ -48,6 +48,7 @@ Use this sequence when SC owns the change-control pass for a refactor or release
|
|
|
48
48
|
```bash
|
|
49
49
|
# 0. Confirm SC's own runbook integrity before recording boundary evidence
|
|
50
50
|
peaks skill runbook peaks-sc --json
|
|
51
|
+
peaks skill presence:set peaks-sc # show persistent skill presence every turn
|
|
51
52
|
|
|
52
53
|
# 1. Derive commit boundaries from OpenSpec when openspec/ exists
|
|
53
54
|
peaks openspec to-rd <change-id> --project <repo> --json
|
|
@@ -71,6 +72,7 @@ peaks sc boundary --slice-id <slice-id> --artifact <artifact-path> --code <code-
|
|
|
71
72
|
# 7. Sync memory and artifacts only when the user or active profile authorizes durable writes
|
|
72
73
|
peaks memory sync --project <repo> --workspace <workspace> --apply --json
|
|
73
74
|
peaks artifacts sync --workspace <workspace> --apply --json
|
|
75
|
+
peaks skill presence:clear # SC complete, remove presence indicator
|
|
74
76
|
```
|
|
75
77
|
|
|
76
78
|
The final two `--apply` calls require explicit authorization. Without it, default to `--dry-run` or omit the sync calls entirely and keep the boundary evidence local under `.peaks/<session-id>/`.
|
|
@@ -92,6 +92,7 @@ The default end-to-end sequence Peaks Solo orchestrates when a user supplies a r
|
|
|
92
92
|
peaks doctor --json
|
|
93
93
|
peaks project dashboard --project <repo> --json # one-call cross-role status
|
|
94
94
|
peaks skill runbook peaks-solo --json # confirm Solo's own runbook is intact + apply-gated
|
|
95
|
+
peaks skill presence:set peaks-solo --mode solo # show persistent skill presence every turn
|
|
95
96
|
|
|
96
97
|
# 1. PRD phase — capture the request as the canonical artifact
|
|
97
98
|
peaks request init --role prd --id <request-id> --project <repo> --apply --json
|
|
@@ -141,6 +142,7 @@ peaks memory extract --project <repo> --artifact <qa-artifact> --dry-run --json
|
|
|
141
142
|
# 8. final snapshot to confirm the workflow really closed
|
|
142
143
|
peaks project dashboard --project <repo> --json
|
|
143
144
|
peaks skill doctor --json # all 7 required skills still healthy?
|
|
145
|
+
peaks skill presence:clear # workflow complete, remove presence indicator
|
|
144
146
|
```
|
|
145
147
|
|
|
146
148
|
Solo's RD↔QA repair loop (`## Mandatory RD QA repair loop` above) applies if QA's verdict is `return-to-rd`. In that case, Solo re-runs phase 3 + phase 4 against the same `<request-id>` instead of starting a new one; the previous artifacts get appended with new transition notes via `--reason` rather than rewritten.
|
|
@@ -105,6 +105,7 @@ Use this sequence when TXT compresses an in-flight workflow into a portable, com
|
|
|
105
105
|
```bash
|
|
106
106
|
# 0. Confirm TXT's own runbook integrity before compressing a handoff
|
|
107
107
|
peaks skill runbook peaks-txt --json
|
|
108
|
+
peaks skill presence:set peaks-txt # show persistent skill presence every turn
|
|
108
109
|
|
|
109
110
|
# 1. Inventory per-role artifacts already produced for the request
|
|
110
111
|
peaks request list --project <repo> --json
|
|
@@ -123,6 +124,7 @@ peaks capabilities --json
|
|
|
123
124
|
# 5. Memory extraction — dry-run by default, apply only when authorized
|
|
124
125
|
peaks memory extract --project <repo> --artifact <artifact-path> --dry-run --json
|
|
125
126
|
peaks memory extract --project <repo> --artifact <artifact-path> --apply --json
|
|
127
|
+
peaks skill presence:clear # handoff capsule complete, remove presence indicator
|
|
126
128
|
```
|
|
127
129
|
|
|
128
130
|
The final `--apply` call requires explicit user or profile authorization. Without it, keep the capsule under `.peaks/<session-id>/txt/` and reference artifact paths from other roles instead of duplicating their content.
|
package/skills/peaks-ui/SKILL.md
CHANGED
|
@@ -30,6 +30,7 @@ The default sequence the UI skill should execute. Skip steps that do not apply;
|
|
|
30
30
|
```bash
|
|
31
31
|
# 0. confirm UI's own runbook integrity before driving any phase
|
|
32
32
|
peaks skill runbook peaks-ui --json
|
|
33
|
+
peaks skill presence:set peaks-ui # show persistent skill presence every turn
|
|
33
34
|
|
|
34
35
|
# 1. capture the UI request as a durable artifact tied to the same PRD request id
|
|
35
36
|
peaks request init --role ui --id <request-id> --project <repo> --json
|
|
@@ -55,6 +56,7 @@ peaks mcp apply --capability playwright-mcp.browser-validation --yes --json #
|
|
|
55
56
|
# 5. hand off to RD / QA via the cross-linked request id
|
|
56
57
|
peaks request list --project <repo> --json
|
|
57
58
|
peaks request show <request-id> --role ui --project <repo> --json
|
|
59
|
+
peaks skill presence:clear # handoff complete, remove presence indicator
|
|
58
60
|
```
|
|
59
61
|
|
|
60
62
|
Handoff is blocked until the UI artifact's `state` reaches `direction-locked` or `handed-off`.
|