peaks-cli 1.3.8 → 1.4.0
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 +27 -0
- package/dist/src/cli/commands/project-commands.js +58 -1
- package/dist/src/cli/commands/request-commands.js +93 -3
- package/dist/src/cli/commands/retrospective-commands.d.ts +3 -0
- package/dist/src/cli/commands/retrospective-commands.js +113 -0
- package/dist/src/cli/commands/skill-scope-commands.d.ts +49 -0
- package/dist/src/cli/commands/skill-scope-commands.js +305 -0
- package/dist/src/cli/commands/workflow-commands.js +1 -1
- package/dist/src/cli/commands/workflow-plan-commands.d.ts +39 -0
- package/dist/src/cli/commands/workflow-plan-commands.js +163 -0
- package/dist/src/cli/program.js +8 -0
- package/dist/src/services/doctor/doctor-service.d.ts +40 -0
- package/dist/src/services/doctor/doctor-service.js +160 -0
- package/dist/src/services/hooks/presence-marker-detector.d.ts +16 -0
- package/dist/src/services/hooks/presence-marker-detector.js +105 -0
- package/dist/src/services/memory/project-memory-service.d.ts +19 -0
- package/dist/src/services/memory/project-memory-service.js +33 -0
- package/dist/src/services/retrospective/migrate-from-md.d.ts +37 -0
- package/dist/src/services/retrospective/migrate-from-md.js +528 -0
- package/dist/src/services/retrospective/retrospective-index.d.ts +37 -0
- package/dist/src/services/retrospective/retrospective-index.js +110 -0
- package/dist/src/services/retrospective/retrospective-show.d.ts +40 -0
- package/dist/src/services/retrospective/retrospective-show.js +109 -0
- package/dist/src/services/skill-scope/adapters/_stub-helper.d.ts +39 -0
- package/dist/src/services/skill-scope/adapters/_stub-helper.js +98 -0
- package/dist/src/services/skill-scope/adapters/claude-code.d.ts +59 -0
- package/dist/src/services/skill-scope/adapters/claude-code.js +304 -0
- package/dist/src/services/skill-scope/adapters/codex.d.ts +2 -0
- package/dist/src/services/skill-scope/adapters/codex.js +12 -0
- package/dist/src/services/skill-scope/adapters/cursor.d.ts +2 -0
- package/dist/src/services/skill-scope/adapters/cursor.js +13 -0
- package/dist/src/services/skill-scope/adapters/qoder.d.ts +2 -0
- package/dist/src/services/skill-scope/adapters/qoder.js +13 -0
- package/dist/src/services/skill-scope/adapters/tongyi.d.ts +2 -0
- package/dist/src/services/skill-scope/adapters/tongyi.js +13 -0
- package/dist/src/services/skill-scope/adapters/trae.d.ts +2 -0
- package/dist/src/services/skill-scope/adapters/trae.js +12 -0
- package/dist/src/services/skill-scope/detect.d.ts +75 -0
- package/dist/src/services/skill-scope/detect.js +480 -0
- package/dist/src/services/skill-scope/registry.d.ts +41 -0
- package/dist/src/services/skill-scope/registry.js +83 -0
- package/dist/src/services/skill-scope/source-of-truth.d.ts +44 -0
- package/dist/src/services/skill-scope/source-of-truth.js +118 -0
- package/dist/src/services/skill-scope/types.d.ts +176 -0
- package/dist/src/services/skill-scope/types.js +74 -0
- package/dist/src/services/standards/migrate-service.d.ts +63 -0
- package/dist/src/services/standards/migrate-service.js +193 -0
- package/dist/src/services/standards/project-standards-service.js +1 -23
- package/dist/src/services/workflow/artifact-paths.d.ts +59 -0
- package/dist/src/services/workflow/artifact-paths.js +127 -0
- package/dist/src/services/workflow/pipeline-verify-service.d.ts +6 -0
- package/dist/src/services/workflow/pipeline-verify-service.js +49 -4
- package/dist/src/services/workflow/plan-reader.d.ts +29 -0
- package/dist/src/services/workflow/plan-reader.js +158 -0
- package/dist/src/services/workflow/plan-refresher.d.ts +32 -0
- package/dist/src/services/workflow/plan-refresher.js +353 -0
- package/dist/src/services/workflow/plan-trigger-detector.d.ts +55 -0
- package/dist/src/services/workflow/plan-trigger-detector.js +142 -0
- package/dist/src/shared/format-md-compact.d.ts +32 -0
- package/dist/src/shared/format-md-compact.js +297 -0
- package/dist/src/shared/stale-policy.d.ts +67 -0
- package/dist/src/shared/stale-policy.js +85 -0
- package/dist/src/shared/version.d.ts +1 -1
- package/dist/src/shared/version.js +1 -1
- package/package.json +3 -2
- package/schemas/doctor-report.schema.json +2 -2
- package/skills/peaks-qa/SKILL.md +103 -507
- package/skills/peaks-qa/references/artifact-per-request.md +7 -79
- package/skills/peaks-qa/references/browser-validation-contracts.md +51 -0
- package/skills/peaks-qa/references/codegraph-regression-focus.md +5 -0
- package/skills/peaks-qa/references/external-capability-guidance.md +9 -0
- package/skills/peaks-qa/references/qa-compact-handoff.md +3 -0
- package/skills/peaks-qa/references/qa-context-governance.md +24 -0
- package/skills/peaks-qa/references/qa-fanout-contract.md +8 -0
- package/skills/peaks-qa/references/qa-gstack-integration.md +7 -0
- package/skills/peaks-qa/references/qa-local-artifacts.md +3 -0
- package/skills/peaks-qa/references/qa-matt-pocock-integration.md +9 -0
- package/skills/peaks-qa/references/qa-perf-test-plan.md +67 -0
- package/skills/peaks-qa/references/qa-refactor-role.md +3 -0
- package/skills/peaks-qa/references/qa-runbook.md +74 -0
- package/skills/peaks-qa/references/qa-security-test-plan.md +73 -0
- package/skills/peaks-qa/references/qa-skill-presence.md +22 -0
- package/skills/peaks-qa/references/qa-standards-preflight.md +8 -0
- package/skills/peaks-qa/references/qa-sub-agent-dispatch.md +38 -0
- package/skills/peaks-qa/references/qa-transition-gates.md +83 -0
- package/skills/peaks-qa/references/requirement-boundary-recheck.md +9 -0
- package/skills/peaks-qa/references/test-case-generation.md +27 -0
- package/skills/peaks-qa/references/test-report-output.md +14 -0
- package/skills/peaks-rd/SKILL.md +85 -612
- package/skills/peaks-rd/references/artifact-and-standards-output.md +9 -0
- package/skills/peaks-rd/references/artifact-per-request.md +20 -0
- package/skills/peaks-rd/references/browser-self-test-contracts.md +29 -0
- package/skills/peaks-rd/references/codegraph-project-analysis.md +5 -0
- package/skills/peaks-rd/references/compact-handoff.md +3 -0
- package/skills/peaks-rd/references/external-references.md +11 -0
- package/skills/peaks-rd/references/frontend-project-generation.md +11 -0
- package/skills/peaks-rd/references/library-version-awareness.md +30 -0
- package/skills/peaks-rd/references/mandatory-perf-baseline.md +42 -0
- package/skills/peaks-rd/references/mandatory-tech-doc.md +18 -0
- package/skills/peaks-rd/references/matt-pocock-integration.md +11 -0
- package/skills/peaks-rd/references/mock-data-placement.md +40 -0
- package/skills/peaks-rd/references/parallel-review-fanout.md +81 -0
- package/skills/peaks-rd/references/rd-context-governance.md +36 -0
- package/skills/peaks-rd/references/rd-gstack-integration.md +16 -0
- package/skills/peaks-rd/references/rd-runbook.md +125 -0
- package/skills/peaks-rd/references/rd-standards-preflight.md +8 -0
- package/skills/peaks-rd/references/rd-sub-agent-dispatch.md +39 -0
- package/skills/peaks-rd/references/rd-transition-gates.md +1 -1
- package/skills/peaks-rd/references/skill-presence-and-title.md +22 -0
- package/skills/peaks-solo/SKILL.md +87 -595
- package/skills/peaks-solo/references/anchoring-and-session-info.md +25 -0
- package/skills/peaks-solo/references/boundaries.md +21 -0
- package/skills/peaks-solo/references/codegraph-orchestration.md +5 -0
- package/skills/peaks-solo/references/completion-handoff.md +16 -0
- package/skills/peaks-solo/references/context-governance.md +51 -0
- package/skills/peaks-solo/references/external-references.md +17 -0
- package/skills/peaks-solo/references/frontend-only-mode.md +14 -0
- package/skills/peaks-solo/references/gstack-integration.md +7 -0
- package/skills/peaks-solo/references/local-artifact-workspace.md +79 -0
- package/skills/peaks-solo/references/micro-cycle.md +68 -0
- package/skills/peaks-solo/references/mode-selection.md +21 -0
- package/skills/peaks-solo/references/openspec-workflow.md +43 -0
- package/skills/peaks-solo/references/project-memory-loading.md +17 -0
- package/skills/peaks-solo/references/quality-gate-cheatsheet.md +13 -0
- package/skills/peaks-solo/references/resume-detection.md +63 -0
- package/skills/peaks-solo/references/runbook.md +1 -1
- package/skills/peaks-solo/references/skill-presence-and-title.md +31 -0
- package/skills/peaks-solo/references/standards-preflight.md +23 -0
- package/skills/peaks-solo/references/sub-agent-dispatch.md +46 -0
- package/skills/peaks-solo/references/swarm-dispatch-contract.md +56 -0
|
@@ -5,12 +5,14 @@ import { getArtifactWorkspaceStatus, planArtifactSync } from '../../services/art
|
|
|
5
5
|
import { executeProjectMemoryBackup, executeProjectMemoryExtract, summarizeProjectMemoryBackupResult, summarizeProjectMemoryExtractResult } from '../../services/memory/project-memory-service.js';
|
|
6
6
|
import { summarizeProjectStandardsInitResult, summarizeProjectStandardsUpdateResult } from '../../services/standards/project-standards-service.js';
|
|
7
7
|
import { executeProjectStandardsInitIdeAware, executeProjectStandardsUpdateIdeAware } from '../../services/standards/ide-aware-standards-service.js';
|
|
8
|
+
import { migrateStandards } from '../../services/standards/migrate-service.js';
|
|
8
9
|
import { listProfiles } from '../../services/profiles/profile-service.js';
|
|
9
10
|
import { planProxyTest } from '../../services/proxy/proxy-service.js';
|
|
10
11
|
import { runDoctor } from '../../services/doctor/doctor-service.js';
|
|
11
12
|
import { listSkills } from '../../services/skills/skill-registry.js';
|
|
12
13
|
import { inspectSkillRunbook } from '../../services/skills/skill-runbook-service.js';
|
|
13
14
|
import { setSkillPresence, clearSkillPresence, getSkillPresence, isSkillPresenceMode, touchSkillHeartbeat } from '../../services/skills/skill-presence-service.js';
|
|
15
|
+
import { detectPresenceMarker } from '../../services/hooks/presence-marker-detector.js';
|
|
14
16
|
import { getSessionId, getSessionMeta, rotateSessionBinding, setSessionMeta, setSessionTitle, listSessionMetas } from '../../services/session/session-manager.js';
|
|
15
17
|
import { resolveCanonicalProjectRoot } from '../../services/config/config-service.js';
|
|
16
18
|
import { findProjectRoot } from '../../services/config/config-safety.js';
|
|
@@ -198,6 +200,16 @@ export function registerCoreAndArtifactCommands(program, io) {
|
|
|
198
200
|
lastHeartbeat: updated.lastHeartbeat
|
|
199
201
|
}), options.json);
|
|
200
202
|
});
|
|
203
|
+
addJsonOption(skill
|
|
204
|
+
.command('detect-marker-loss')
|
|
205
|
+
.description('Detect whether the latest assistant message lost the Peaks-Cli status header while a peaks skill is still active (slice 028 detection primitive).')
|
|
206
|
+
.option('--project <path>', 'project root path (auto-detected from cwd when omitted)')
|
|
207
|
+
.option('--message <text>', 'latest assistant message text to scan (defaults to reading the most recent LLM response from the stdin pipe, or empty string when no pipe is attached)')).action((options) => {
|
|
208
|
+
const projectRoot = options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
209
|
+
const message = options.message ?? '';
|
|
210
|
+
const result = detectPresenceMarker({ project: projectRoot, latestAssistantMessage: message });
|
|
211
|
+
printResult(io, ok('skill.detect-marker-loss', result), options.json);
|
|
212
|
+
});
|
|
201
213
|
const session = program.command('session').description('Manage Peaks session directories');
|
|
202
214
|
addJsonOption(session
|
|
203
215
|
.command('list')
|
|
@@ -449,6 +461,21 @@ export function registerCoreAndArtifactCommands(program, io) {
|
|
|
449
461
|
process.exitCode = 1;
|
|
450
462
|
}
|
|
451
463
|
});
|
|
464
|
+
addJsonOption(standards
|
|
465
|
+
.command('migrate')
|
|
466
|
+
.description('Rewrite a consumer project CLAUDE.md to drop the legacy heartbeat block (slice 028). Dry-run by default; pass --apply to write.')
|
|
467
|
+
.option('--project <path>', 'target project root')
|
|
468
|
+
.option('--apply', 'rewrite the legacy block in place; default is dry-run')).action((options) => {
|
|
469
|
+
const projectRoot = options.project ?? process.cwd();
|
|
470
|
+
try {
|
|
471
|
+
const result = migrateStandards({ project: projectRoot, apply: options.apply === true });
|
|
472
|
+
printResult(io, ok('standards.migrate', result.data, [], result.data.nextActions), options.json);
|
|
473
|
+
}
|
|
474
|
+
catch (error) {
|
|
475
|
+
printResult(io, fail('standards.migrate', 'STANDARDS_MIGRATE_FAILED', getErrorMessage(error), { file: null, foundOldBlock: false, wouldChange: false, applied: false, before: null, after: null, nextActions: [] }, [getErrorMessage(error)]), options.json);
|
|
476
|
+
process.exitCode = 1;
|
|
477
|
+
}
|
|
478
|
+
});
|
|
452
479
|
const memory = program.command('memory').description('Manage project-local Peaks memory');
|
|
453
480
|
addJsonOption(memory
|
|
454
481
|
.command('extract')
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { loadProjectDashboard } from '../../services/dashboard/project-dashboard-service.js';
|
|
2
2
|
import { generateProjectContext, readProjectContext } from '../../services/memory/project-context-service.js';
|
|
3
|
-
import { extractSessionMemories, readMemoryIndex, readProjectMemories } from '../../services/memory/project-memory-service.js';
|
|
3
|
+
import { extractSessionMemories, readMemoryIndex, readProjectMemories, readProjectMemoryBody } from '../../services/memory/project-memory-service.js';
|
|
4
|
+
import { applyStalePolicy, DEFAULT_STALE_DAYS } from '../../shared/stale-policy.js';
|
|
5
|
+
import { formatMdCompact } from '../../shared/format-md-compact.js';
|
|
4
6
|
import { fail, ok } from '../../shared/result.js';
|
|
5
7
|
import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
|
|
6
8
|
export function registerProjectCommands(program, io) {
|
|
@@ -158,4 +160,59 @@ export function registerProjectCommands(program, io) {
|
|
|
158
160
|
process.exitCode = 1;
|
|
159
161
|
}
|
|
160
162
|
});
|
|
163
|
+
// --- Show one project memory's body (R3: default = compact) ---
|
|
164
|
+
addJsonOption(project
|
|
165
|
+
.command('memories:show <name>')
|
|
166
|
+
.description('Show one project memory body by name. Default format is `compact` (LLM-primary); pass --pretty for the disk verbatim. Stale entries (default ≥30 days) are excluded; pass --include-stale or --stale-days <N> to override.')
|
|
167
|
+
.requiredOption('--project <path>', 'target project root')
|
|
168
|
+
.option('--pretty', 'return the on-disk body verbatim; overrides the compact default')
|
|
169
|
+
.option('--include-stale', 'include stale entries (the default excludes them)')
|
|
170
|
+
.option('--stale-days <n>', 'override the 30-day stale threshold (must be > 0)', (value) => Number(value))).action((name, options) => {
|
|
171
|
+
try {
|
|
172
|
+
const memory = readProjectMemoryBody(options.project, name);
|
|
173
|
+
if (memory === null) {
|
|
174
|
+
printResult(io, fail('project.memories:show', 'MEMORY_NOT_FOUND', `memory ${name} not found in .peaks/memory`, { name, projectRoot: options.project }, ['Run `peaks project memories --json` to see available names']), options.json);
|
|
175
|
+
process.exitCode = 1;
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
// Compute the stale decision. R4: stale is computed at CLI load time
|
|
179
|
+
// only; the source `.md` file is never modified.
|
|
180
|
+
const updatedAt = memory.updatedAt;
|
|
181
|
+
const thresholdDays = options.staleDays !== undefined && Number.isFinite(options.staleDays) && options.staleDays > 0
|
|
182
|
+
? options.staleDays
|
|
183
|
+
: DEFAULT_STALE_DAYS;
|
|
184
|
+
const policy = applyStalePolicy([{ name: memory.name, updatedAt }], {
|
|
185
|
+
thresholdDays,
|
|
186
|
+
includeStale: options.includeStale === true
|
|
187
|
+
});
|
|
188
|
+
if (policy.entries.length === 0) {
|
|
189
|
+
const ageDays = policy.entries.length === 0 && policy.droppedCount > 0
|
|
190
|
+
? applyStalePolicy([{ name: memory.name, updatedAt }], { thresholdDays, includeStale: true }).entries[0]?.ageDays ?? 0
|
|
191
|
+
: 0;
|
|
192
|
+
printResult(io, fail('project.memories:show', 'MEMORY_STALE', `memory ${name} is stale (age ${ageDays} days > ${thresholdDays} day threshold); pass --include-stale to override`, { name, ageDays, thresholdDays }, ['Pass --include-stale to load stale memories; pass --stale-days <N> to override the threshold']), options.json);
|
|
193
|
+
process.exitCode = 1;
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const ageDays = policy.entries[0]?.ageDays ?? 0;
|
|
197
|
+
const isStale = policy.entries[0]?.stale ?? false;
|
|
198
|
+
const format = options.pretty === true ? 'pretty' : 'compact';
|
|
199
|
+
const body = format === 'pretty' ? memory.body : formatMdCompact(memory.body);
|
|
200
|
+
printResult(io, ok('project.memories:show', {
|
|
201
|
+
name: memory.name,
|
|
202
|
+
title: memory.title,
|
|
203
|
+
kind: memory.kind,
|
|
204
|
+
sourcePath: memory.filePath,
|
|
205
|
+
updatedAt,
|
|
206
|
+
ageDays,
|
|
207
|
+
stale: isStale,
|
|
208
|
+
body,
|
|
209
|
+
format,
|
|
210
|
+
bodyBytes: Buffer.byteLength(body, 'utf8')
|
|
211
|
+
}), options.json);
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
printResult(io, fail('project.memories:show', 'PROJECT_MEMORY_SHOW_FAILED', getErrorMessage(error), { name, projectRoot: options.project }, ['Check the project path and .peaks/memory directory']), options.json);
|
|
215
|
+
process.exitCode = 1;
|
|
216
|
+
}
|
|
217
|
+
});
|
|
161
218
|
}
|
|
@@ -5,7 +5,82 @@ import { recordBypass, isBypassLimitReached, MAX_BYPASSES_PER_SESSION } from '..
|
|
|
5
5
|
import { lintRequestArtifact } from '../../services/artifacts/artifact-lint-service.js';
|
|
6
6
|
import { getRepairCycleStatus } from '../../services/artifacts/repair-cycle-service.js';
|
|
7
7
|
import { fail, ok } from '../../shared/result.js';
|
|
8
|
+
import { formatMdCompact } from '../../shared/format-md-compact.js';
|
|
8
9
|
import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
|
|
10
|
+
/**
|
|
11
|
+
* Per-artifact default format. Only PRD and tech-doc are user-review
|
|
12
|
+
* surfaces; all other RD/QA/TXT artifacts default to compact. The
|
|
13
|
+
* `--pretty` / `--compact` flags override uniformly. See tech-doc
|
|
14
|
+
* §1.3.
|
|
15
|
+
*/
|
|
16
|
+
const DEFAULT_FORMAT_BY_ARTIFACT = {
|
|
17
|
+
prd: 'pretty',
|
|
18
|
+
'tech-doc': 'pretty',
|
|
19
|
+
'code-review': 'compact',
|
|
20
|
+
'security-review': 'compact',
|
|
21
|
+
'perf-baseline': 'compact',
|
|
22
|
+
'bug-analysis': 'compact',
|
|
23
|
+
'test-cases': 'compact',
|
|
24
|
+
'test-reports': 'compact',
|
|
25
|
+
'security-findings': 'compact',
|
|
26
|
+
'performance-findings': 'compact',
|
|
27
|
+
handoff: 'compact'
|
|
28
|
+
};
|
|
29
|
+
function resolveDefaultFormat(artifactName) {
|
|
30
|
+
return DEFAULT_FORMAT_BY_ARTIFACT[artifactName] ?? 'compact';
|
|
31
|
+
}
|
|
32
|
+
function applyPerArtifactFormat(envelope, override) {
|
|
33
|
+
if (override === null || envelope === null || typeof envelope !== 'object')
|
|
34
|
+
return envelope;
|
|
35
|
+
// The service returns `{ id, sessionId, role, body, ... }` for a
|
|
36
|
+
// single-artifact show. The per-artifact `body` field is the only
|
|
37
|
+
// thing that changes; we attach a `format` field to surface the
|
|
38
|
+
// choice to the caller. Slice 023 (R3) AC6 / AC7.
|
|
39
|
+
const obj = envelope;
|
|
40
|
+
if (typeof obj.body === 'string') {
|
|
41
|
+
return {
|
|
42
|
+
...obj,
|
|
43
|
+
body: override === 'compact' ? formatMdCompact(obj.body) : obj.body,
|
|
44
|
+
format: override
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return envelope;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Map a request-artifact envelope to a `DEFAULT_FORMAT_BY_ARTIFACT` key.
|
|
51
|
+
* The service returns the path of the artifact in `path`; the role +
|
|
52
|
+
* filename stem gives us the artifact name. Falls back to the role
|
|
53
|
+
* itself when no `path` field is present.
|
|
54
|
+
*/
|
|
55
|
+
function inferArtifactName(envelope, role) {
|
|
56
|
+
if (envelope === null || typeof envelope !== 'object')
|
|
57
|
+
return role;
|
|
58
|
+
const obj = envelope;
|
|
59
|
+
const pathField = typeof obj.path === 'string' ? obj.path : '';
|
|
60
|
+
// Path looks like `.peaks/_runtime/<sid>/<role>/requests/<file>.md`.
|
|
61
|
+
// The role is the second-to-last directory; the file stem is the
|
|
62
|
+
// last segment. For RD role, the artifact is one of {code-review,
|
|
63
|
+
// security-review, perf-baseline, bug-analysis, tech-doc}; the file
|
|
64
|
+
// stem usually carries the artifact name (e.g. `tech-doc.md`,
|
|
65
|
+
// `code-review-002.md`). We strip the trailing `-<digits>` suffix
|
|
66
|
+
// when present.
|
|
67
|
+
const stem = pathField.split(/[\\/]/).pop()?.replace(/\.md$/, '') ?? '';
|
|
68
|
+
if (stem.length > 0) {
|
|
69
|
+
// Try the stem verbatim first.
|
|
70
|
+
if (DEFAULT_FORMAT_BY_ARTIFACT[stem] !== undefined)
|
|
71
|
+
return stem;
|
|
72
|
+
// Strip a trailing -<digits> (e.g. code-review-002 -> code-review).
|
|
73
|
+
const trimmed = stem.replace(/-\d+$/, '');
|
|
74
|
+
if (DEFAULT_FORMAT_BY_ARTIFACT[trimmed] !== undefined)
|
|
75
|
+
return trimmed;
|
|
76
|
+
// Last-ditch: match by prefix.
|
|
77
|
+
for (const key of Object.keys(DEFAULT_FORMAT_BY_ARTIFACT)) {
|
|
78
|
+
if (stem.startsWith(key))
|
|
79
|
+
return key;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return role;
|
|
83
|
+
}
|
|
9
84
|
const VALID_ROLES = ['prd', 'ui', 'rd', 'qa', 'sc'];
|
|
10
85
|
function parseRole(value) {
|
|
11
86
|
if (!VALID_ROLES.includes(value)) {
|
|
@@ -119,11 +194,13 @@ export function registerRequestCommands(program, io) {
|
|
|
119
194
|
});
|
|
120
195
|
addJsonOption(request
|
|
121
196
|
.command('show')
|
|
122
|
-
.description('Show a single per-request artifact, optionally scoped to a session')
|
|
197
|
+
.description('Show a single per-request artifact, optionally scoped to a session. R3: default body format is per-artifact (PRD/tech-doc pretty; everything else compact); pass --pretty or --compact to override uniformly.')
|
|
123
198
|
.argument('<request-id>', 'request id, e.g. 2026-05-23-add-foo')
|
|
124
199
|
.requiredOption('--role <role>', `target role (${VALID_ROLES.join(' | ')})`, parseRole)
|
|
125
200
|
.requiredOption('--project <path>', 'target project root')
|
|
126
|
-
.option('--session-id <session>', 'restrict to a specific session id')
|
|
201
|
+
.option('--session-id <session>', 'restrict to a specific session id')
|
|
202
|
+
.option('--pretty', 'force the body to render pretty (overrides the per-artifact default)')
|
|
203
|
+
.option('--compact', 'force the body to render compact (overrides the per-artifact default)')).action(async (requestId, options) => {
|
|
127
204
|
try {
|
|
128
205
|
const showOptions = {
|
|
129
206
|
projectRoot: options.project,
|
|
@@ -139,7 +216,20 @@ export function registerRequestCommands(program, io) {
|
|
|
139
216
|
process.exitCode = 1;
|
|
140
217
|
return;
|
|
141
218
|
}
|
|
142
|
-
|
|
219
|
+
// R3: pick the per-artifact default format and apply the override
|
|
220
|
+
// if either flag is set. Last-flag-wins if both are passed.
|
|
221
|
+
const override = options.compact === true
|
|
222
|
+
? 'compact'
|
|
223
|
+
: options.pretty === true
|
|
224
|
+
? 'pretty'
|
|
225
|
+
: null;
|
|
226
|
+
const artifactName = inferArtifactName(result, options.role);
|
|
227
|
+
const format = override ?? resolveDefaultFormat(artifactName);
|
|
228
|
+
const transformed = applyPerArtifactFormat(result, override ?? format);
|
|
229
|
+
const payload = transformed === result
|
|
230
|
+
? { ...result, format }
|
|
231
|
+
: transformed;
|
|
232
|
+
printResult(io, ok('request.show', payload), options.json);
|
|
143
233
|
}
|
|
144
234
|
catch (error) {
|
|
145
235
|
printResult(io, fail('request.show', 'REQUEST_SHOW_FAILED', getErrorMessage(error), { role: options.role, requestId }, ['Check role, request id, and project path before retrying']), options.json);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { findProjectRoot } from '../../services/config/config-safety.js';
|
|
2
|
+
import { resolveCanonicalProjectRoot } from '../../services/config/config-service.js';
|
|
3
|
+
import { loadRetrospectiveIndex } from '../../services/retrospective/retrospective-index.js';
|
|
4
|
+
import { showRetrospective } from '../../services/retrospective/retrospective-show.js';
|
|
5
|
+
import { migrateRetrospectiveFromMd } from '../../services/retrospective/migrate-from-md.js';
|
|
6
|
+
import { fail, ok } from '../../shared/result.js';
|
|
7
|
+
import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
|
|
8
|
+
export function registerRetrospectiveCommands(program, io) {
|
|
9
|
+
const retrospective = program.command('retrospective').description('Read the peaks retrospective index (R3: index.json, not the legacy <id>/ MD tree)');
|
|
10
|
+
addJsonOption(retrospective
|
|
11
|
+
.command('index')
|
|
12
|
+
.description('List all retrospective entries from .peaks/retrospective/index.json (R3: replaces the per-workflow MD dirs)')
|
|
13
|
+
.option('--project <path>', 'target project root (defaults to git root or cwd)')).action((options) => {
|
|
14
|
+
const projectRoot = options.project !== undefined
|
|
15
|
+
? resolveCanonicalProjectRoot(options.project)
|
|
16
|
+
: (findProjectRoot(process.cwd()) ?? process.cwd());
|
|
17
|
+
try {
|
|
18
|
+
const result = loadRetrospectiveIndex(projectRoot);
|
|
19
|
+
const warnings = result.warning === null ? [] : [result.warning];
|
|
20
|
+
printResult(io, ok('retrospective.index', {
|
|
21
|
+
indexPath: result.indexPath,
|
|
22
|
+
source: result.source,
|
|
23
|
+
total: result.totalCount,
|
|
24
|
+
entries: result.entries
|
|
25
|
+
}, warnings), options.json);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
printResult(io, fail('retrospective.index', 'RETROSPECTIVE_INDEX_FAILED', getErrorMessage(error), { projectRoot }, ['Check the project path and .peaks/retrospective/index.json']), options.json);
|
|
29
|
+
process.exitCode = 1;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
addJsonOption(retrospective
|
|
33
|
+
.command('show <id>')
|
|
34
|
+
.description('Show one retrospective entry by id. Default format is `compact` (LLM-primary); pass --pretty to get the disk / re-hydrated pretty form.')
|
|
35
|
+
.option('--project <path>', 'target project root (defaults to git root or cwd)')
|
|
36
|
+
.option('--pretty', 'return the pretty form (re-hydrated from source artifacts); overrides the compact default')).action((id, options) => {
|
|
37
|
+
const projectRoot = options.project !== undefined
|
|
38
|
+
? resolveCanonicalProjectRoot(options.project)
|
|
39
|
+
: (findProjectRoot(process.cwd()) ?? process.cwd());
|
|
40
|
+
try {
|
|
41
|
+
const format = options.pretty === true ? 'pretty' : 'compact';
|
|
42
|
+
const result = showRetrospective({ projectRoot, id, format });
|
|
43
|
+
if (!result.ok) {
|
|
44
|
+
const suggestions = [];
|
|
45
|
+
if (result.code === 'INDEX_MISSING')
|
|
46
|
+
suggestions.push('Run `peaks retrospective migrate --apply` to build the index');
|
|
47
|
+
if (result.code === 'NOT_FOUND')
|
|
48
|
+
suggestions.push('Run `peaks retrospective index --json` to see available ids');
|
|
49
|
+
if (result.code === 'ARTIFACT_MISSING' || result.missingArtifacts !== undefined) {
|
|
50
|
+
suggestions.push('Re-hydrate from the legacy archive at .peaks/_archive/retrospective-2026-06-09-pre-r3.tar.gz');
|
|
51
|
+
}
|
|
52
|
+
printResult(io, fail('retrospective.show', result.code, result.message, { id, projectRoot, ...(result.missingArtifacts !== undefined ? { missingArtifacts: result.missingArtifacts } : {}) }, suggestions), options.json);
|
|
53
|
+
process.exitCode = 1;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
printResult(io, ok('retrospective.show', {
|
|
57
|
+
id: result.entry.id,
|
|
58
|
+
sessionId: result.entry.sessionId,
|
|
59
|
+
sliceId: result.entry.sliceId ?? null,
|
|
60
|
+
type: result.entry.type,
|
|
61
|
+
title: result.entry.title,
|
|
62
|
+
summary: result.entry.summary,
|
|
63
|
+
outcome: result.entry.outcome,
|
|
64
|
+
keyDecisions: result.entry.keyDecisions,
|
|
65
|
+
lessonsLearned: result.entry.lessonsLearned,
|
|
66
|
+
artifactPaths: result.entry.artifactPaths,
|
|
67
|
+
updatedAt: result.entry.updatedAt,
|
|
68
|
+
body: result.body,
|
|
69
|
+
format: result.format
|
|
70
|
+
}, result.warnings), options.json);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
printResult(io, fail('retrospective.show', 'RETROSPECTIVE_SHOW_FAILED', getErrorMessage(error), { id, projectRoot }, ['Check the project path and id']), options.json);
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
addJsonOption(retrospective
|
|
78
|
+
.command('migrate')
|
|
79
|
+
.description('One-time migration from per-workflow .peaks/retrospective/<id>/*.md dirs to a single .peaks/retrospective/index.json + .peaks/_archive/retrospective-2026-06-09-pre-r3.tar.gz archive. Dry-run by default; --apply is destructive.')
|
|
80
|
+
.option('--project <path>', 'target project root (defaults to git root or cwd)')
|
|
81
|
+
.option('--apply', 'write the index.json + archive + delete legacy MDs (default: dry-run preview)')
|
|
82
|
+
.option('--include-failed', 'include malformed MDs as best-effort entries; default is to skip + warn')
|
|
83
|
+
.option('--expected-entries <n>', 'override the default expected entry count (88) for the no-op check', (value) => Number(value))).action((options) => {
|
|
84
|
+
const projectRoot = options.project !== undefined
|
|
85
|
+
? resolveCanonicalProjectRoot(options.project)
|
|
86
|
+
: (findProjectRoot(process.cwd()) ?? process.cwd());
|
|
87
|
+
try {
|
|
88
|
+
const result = migrateRetrospectiveFromMd({
|
|
89
|
+
projectRoot,
|
|
90
|
+
apply: options.apply === true,
|
|
91
|
+
includeFailed: options.includeFailed === true,
|
|
92
|
+
...(options.expectedEntries !== undefined && Number.isFinite(options.expectedEntries) ? { expectedEntries: options.expectedEntries } : {})
|
|
93
|
+
});
|
|
94
|
+
const exitCode = result.status === 'failed' ? 1 : 0;
|
|
95
|
+
printResult(io, ok('retrospective.migrate', {
|
|
96
|
+
status: result.status,
|
|
97
|
+
indexPath: result.indexPath,
|
|
98
|
+
archivePath: result.archivePath,
|
|
99
|
+
totalLegacyDirs: result.totalLegacyDirs,
|
|
100
|
+
totalLegacyMds: result.totalLegacyMds,
|
|
101
|
+
parsedEntries: result.parsedEntries,
|
|
102
|
+
failedEntries: result.failedEntries,
|
|
103
|
+
archiveVerified: result.archiveVerified
|
|
104
|
+
}, result.warnings), options.json);
|
|
105
|
+
if (exitCode !== 0)
|
|
106
|
+
process.exitCode = exitCode;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
printResult(io, fail('retrospective.migrate', 'RETROSPECTIVE_MIGRATE_FAILED', getErrorMessage(error), { projectRoot }, ['Check the project path and .peaks/retrospective/ directory']), options.json);
|
|
110
|
+
process.exitCode = 1;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `peaks skill scope` CLI surface (slice 025.1).
|
|
3
|
+
*
|
|
4
|
+
* Four subcommands (mutually exclusive):
|
|
5
|
+
* - `--detect` — dry-run; prints the relevance matrix, never touches files.
|
|
6
|
+
* - `--apply` — writes the source-of-truth + IDE-native config.
|
|
7
|
+
* - `--show` — reads the source-of-truth + native config back.
|
|
8
|
+
* - `--reset` — removes the source-of-truth + IDE-native config.
|
|
9
|
+
*
|
|
10
|
+
* Exit code matrix (tech-doc §6.3):
|
|
11
|
+
* 0 success
|
|
12
|
+
* 1 uncaught error
|
|
13
|
+
* 2 invalid usage (missing/incompatible flags)
|
|
14
|
+
* 3 source-of-truth written but adapter returned NOT_SUPPORTED
|
|
15
|
+
* 4 adapter failure other than NOT_SUPPORTED
|
|
16
|
+
*/
|
|
17
|
+
import { Command } from 'commander';
|
|
18
|
+
import { type ResultEnvelope } from '../../shared/result.js';
|
|
19
|
+
import { type ProgramIO } from '../cli-helpers.js';
|
|
20
|
+
export type SkillScopeAction = 'detect' | 'apply' | 'show' | 'reset';
|
|
21
|
+
export interface RunSkillScopeInput {
|
|
22
|
+
readonly subcommand: SkillScopeAction;
|
|
23
|
+
readonly project: string;
|
|
24
|
+
readonly strict?: boolean;
|
|
25
|
+
readonly loose?: boolean;
|
|
26
|
+
readonly ide?: string;
|
|
27
|
+
readonly shadowFallback?: boolean;
|
|
28
|
+
readonly json?: boolean;
|
|
29
|
+
/** Test seam: override the detected allowlist (CLI re-adds peaks-* per G6). */
|
|
30
|
+
readonly overrideAllowlist?: readonly string[];
|
|
31
|
+
/** Test seam: force the source-of-truth write to fail (simulates atomicity test). */
|
|
32
|
+
readonly simulateSourceOfTruthWriteFailure?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface RunSkillScopeResult {
|
|
35
|
+
readonly exitCode: number;
|
|
36
|
+
readonly envelope: ResultEnvelope<unknown> | null;
|
|
37
|
+
readonly stdout: string;
|
|
38
|
+
readonly stderr: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Programmatic entry point for `peaks skill scope`. Used by the CLI shim
|
|
42
|
+
* AND by the unit tests.
|
|
43
|
+
*/
|
|
44
|
+
export declare function runSkillScopeCommand(input: RunSkillScopeInput): Promise<RunSkillScopeResult>;
|
|
45
|
+
/**
|
|
46
|
+
* Register the `peaks skill scope` subcommand on the `skill` command group.
|
|
47
|
+
* Mutually-exclusive flags: exactly one of --detect / --apply / --show / --reset.
|
|
48
|
+
*/
|
|
49
|
+
export declare function registerSkillScopeCommands(program: Command, io: ProgramIO): void;
|