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.
Files changed (130) hide show
  1. package/dist/src/cli/commands/core-artifact-commands.js +27 -0
  2. package/dist/src/cli/commands/project-commands.js +58 -1
  3. package/dist/src/cli/commands/request-commands.js +93 -3
  4. package/dist/src/cli/commands/retrospective-commands.d.ts +3 -0
  5. package/dist/src/cli/commands/retrospective-commands.js +113 -0
  6. package/dist/src/cli/commands/skill-scope-commands.d.ts +49 -0
  7. package/dist/src/cli/commands/skill-scope-commands.js +305 -0
  8. package/dist/src/cli/commands/workflow-commands.js +1 -1
  9. package/dist/src/cli/commands/workflow-plan-commands.d.ts +39 -0
  10. package/dist/src/cli/commands/workflow-plan-commands.js +163 -0
  11. package/dist/src/cli/program.js +8 -0
  12. package/dist/src/services/doctor/doctor-service.d.ts +40 -0
  13. package/dist/src/services/doctor/doctor-service.js +160 -0
  14. package/dist/src/services/hooks/presence-marker-detector.d.ts +16 -0
  15. package/dist/src/services/hooks/presence-marker-detector.js +105 -0
  16. package/dist/src/services/memory/project-memory-service.d.ts +19 -0
  17. package/dist/src/services/memory/project-memory-service.js +33 -0
  18. package/dist/src/services/retrospective/migrate-from-md.d.ts +37 -0
  19. package/dist/src/services/retrospective/migrate-from-md.js +528 -0
  20. package/dist/src/services/retrospective/retrospective-index.d.ts +37 -0
  21. package/dist/src/services/retrospective/retrospective-index.js +110 -0
  22. package/dist/src/services/retrospective/retrospective-show.d.ts +40 -0
  23. package/dist/src/services/retrospective/retrospective-show.js +109 -0
  24. package/dist/src/services/skill-scope/adapters/_stub-helper.d.ts +39 -0
  25. package/dist/src/services/skill-scope/adapters/_stub-helper.js +98 -0
  26. package/dist/src/services/skill-scope/adapters/claude-code.d.ts +59 -0
  27. package/dist/src/services/skill-scope/adapters/claude-code.js +304 -0
  28. package/dist/src/services/skill-scope/adapters/codex.d.ts +2 -0
  29. package/dist/src/services/skill-scope/adapters/codex.js +12 -0
  30. package/dist/src/services/skill-scope/adapters/cursor.d.ts +2 -0
  31. package/dist/src/services/skill-scope/adapters/cursor.js +13 -0
  32. package/dist/src/services/skill-scope/adapters/qoder.d.ts +2 -0
  33. package/dist/src/services/skill-scope/adapters/qoder.js +13 -0
  34. package/dist/src/services/skill-scope/adapters/tongyi.d.ts +2 -0
  35. package/dist/src/services/skill-scope/adapters/tongyi.js +13 -0
  36. package/dist/src/services/skill-scope/adapters/trae.d.ts +2 -0
  37. package/dist/src/services/skill-scope/adapters/trae.js +12 -0
  38. package/dist/src/services/skill-scope/detect.d.ts +75 -0
  39. package/dist/src/services/skill-scope/detect.js +480 -0
  40. package/dist/src/services/skill-scope/registry.d.ts +41 -0
  41. package/dist/src/services/skill-scope/registry.js +83 -0
  42. package/dist/src/services/skill-scope/source-of-truth.d.ts +44 -0
  43. package/dist/src/services/skill-scope/source-of-truth.js +118 -0
  44. package/dist/src/services/skill-scope/types.d.ts +176 -0
  45. package/dist/src/services/skill-scope/types.js +74 -0
  46. package/dist/src/services/standards/migrate-service.d.ts +63 -0
  47. package/dist/src/services/standards/migrate-service.js +193 -0
  48. package/dist/src/services/standards/project-standards-service.js +1 -23
  49. package/dist/src/services/workflow/artifact-paths.d.ts +59 -0
  50. package/dist/src/services/workflow/artifact-paths.js +127 -0
  51. package/dist/src/services/workflow/pipeline-verify-service.d.ts +6 -0
  52. package/dist/src/services/workflow/pipeline-verify-service.js +49 -4
  53. package/dist/src/services/workflow/plan-reader.d.ts +29 -0
  54. package/dist/src/services/workflow/plan-reader.js +158 -0
  55. package/dist/src/services/workflow/plan-refresher.d.ts +32 -0
  56. package/dist/src/services/workflow/plan-refresher.js +353 -0
  57. package/dist/src/services/workflow/plan-trigger-detector.d.ts +55 -0
  58. package/dist/src/services/workflow/plan-trigger-detector.js +142 -0
  59. package/dist/src/shared/format-md-compact.d.ts +32 -0
  60. package/dist/src/shared/format-md-compact.js +297 -0
  61. package/dist/src/shared/stale-policy.d.ts +67 -0
  62. package/dist/src/shared/stale-policy.js +85 -0
  63. package/dist/src/shared/version.d.ts +1 -1
  64. package/dist/src/shared/version.js +1 -1
  65. package/package.json +3 -2
  66. package/schemas/doctor-report.schema.json +2 -2
  67. package/skills/peaks-qa/SKILL.md +103 -507
  68. package/skills/peaks-qa/references/artifact-per-request.md +7 -79
  69. package/skills/peaks-qa/references/browser-validation-contracts.md +51 -0
  70. package/skills/peaks-qa/references/codegraph-regression-focus.md +5 -0
  71. package/skills/peaks-qa/references/external-capability-guidance.md +9 -0
  72. package/skills/peaks-qa/references/qa-compact-handoff.md +3 -0
  73. package/skills/peaks-qa/references/qa-context-governance.md +24 -0
  74. package/skills/peaks-qa/references/qa-fanout-contract.md +8 -0
  75. package/skills/peaks-qa/references/qa-gstack-integration.md +7 -0
  76. package/skills/peaks-qa/references/qa-local-artifacts.md +3 -0
  77. package/skills/peaks-qa/references/qa-matt-pocock-integration.md +9 -0
  78. package/skills/peaks-qa/references/qa-perf-test-plan.md +67 -0
  79. package/skills/peaks-qa/references/qa-refactor-role.md +3 -0
  80. package/skills/peaks-qa/references/qa-runbook.md +74 -0
  81. package/skills/peaks-qa/references/qa-security-test-plan.md +73 -0
  82. package/skills/peaks-qa/references/qa-skill-presence.md +22 -0
  83. package/skills/peaks-qa/references/qa-standards-preflight.md +8 -0
  84. package/skills/peaks-qa/references/qa-sub-agent-dispatch.md +38 -0
  85. package/skills/peaks-qa/references/qa-transition-gates.md +83 -0
  86. package/skills/peaks-qa/references/requirement-boundary-recheck.md +9 -0
  87. package/skills/peaks-qa/references/test-case-generation.md +27 -0
  88. package/skills/peaks-qa/references/test-report-output.md +14 -0
  89. package/skills/peaks-rd/SKILL.md +85 -612
  90. package/skills/peaks-rd/references/artifact-and-standards-output.md +9 -0
  91. package/skills/peaks-rd/references/artifact-per-request.md +20 -0
  92. package/skills/peaks-rd/references/browser-self-test-contracts.md +29 -0
  93. package/skills/peaks-rd/references/codegraph-project-analysis.md +5 -0
  94. package/skills/peaks-rd/references/compact-handoff.md +3 -0
  95. package/skills/peaks-rd/references/external-references.md +11 -0
  96. package/skills/peaks-rd/references/frontend-project-generation.md +11 -0
  97. package/skills/peaks-rd/references/library-version-awareness.md +30 -0
  98. package/skills/peaks-rd/references/mandatory-perf-baseline.md +42 -0
  99. package/skills/peaks-rd/references/mandatory-tech-doc.md +18 -0
  100. package/skills/peaks-rd/references/matt-pocock-integration.md +11 -0
  101. package/skills/peaks-rd/references/mock-data-placement.md +40 -0
  102. package/skills/peaks-rd/references/parallel-review-fanout.md +81 -0
  103. package/skills/peaks-rd/references/rd-context-governance.md +36 -0
  104. package/skills/peaks-rd/references/rd-gstack-integration.md +16 -0
  105. package/skills/peaks-rd/references/rd-runbook.md +125 -0
  106. package/skills/peaks-rd/references/rd-standards-preflight.md +8 -0
  107. package/skills/peaks-rd/references/rd-sub-agent-dispatch.md +39 -0
  108. package/skills/peaks-rd/references/rd-transition-gates.md +1 -1
  109. package/skills/peaks-rd/references/skill-presence-and-title.md +22 -0
  110. package/skills/peaks-solo/SKILL.md +87 -595
  111. package/skills/peaks-solo/references/anchoring-and-session-info.md +25 -0
  112. package/skills/peaks-solo/references/boundaries.md +21 -0
  113. package/skills/peaks-solo/references/codegraph-orchestration.md +5 -0
  114. package/skills/peaks-solo/references/completion-handoff.md +16 -0
  115. package/skills/peaks-solo/references/context-governance.md +51 -0
  116. package/skills/peaks-solo/references/external-references.md +17 -0
  117. package/skills/peaks-solo/references/frontend-only-mode.md +14 -0
  118. package/skills/peaks-solo/references/gstack-integration.md +7 -0
  119. package/skills/peaks-solo/references/local-artifact-workspace.md +79 -0
  120. package/skills/peaks-solo/references/micro-cycle.md +68 -0
  121. package/skills/peaks-solo/references/mode-selection.md +21 -0
  122. package/skills/peaks-solo/references/openspec-workflow.md +43 -0
  123. package/skills/peaks-solo/references/project-memory-loading.md +17 -0
  124. package/skills/peaks-solo/references/quality-gate-cheatsheet.md +13 -0
  125. package/skills/peaks-solo/references/resume-detection.md +63 -0
  126. package/skills/peaks-solo/references/runbook.md +1 -1
  127. package/skills/peaks-solo/references/skill-presence-and-title.md +31 -0
  128. package/skills/peaks-solo/references/standards-preflight.md +23 -0
  129. package/skills/peaks-solo/references/sub-agent-dispatch.md +46 -0
  130. package/skills/peaks-solo/references/swarm-dispatch-contract.md +56 -0
@@ -270,6 +270,121 @@ function findDestructiveApplyLines(section) {
270
270
  const lines = section.split(/\r?\n/);
271
271
  return lines.filter((line) => DESTRUCTIVE_APPLY_PATTERNS.some((pattern) => pattern.test(line)));
272
272
  }
273
+ // ---------------------------------------------------------------------------
274
+ // 2026-06-10 — gateguard-fact-force integration check (NOT a peaks-cli hook).
275
+ //
276
+ // The `gateguard-fact-force` hook is a third-party PreToolUse hook that
277
+ // fires on Edit / Write / MultiEdit and demands a 4-fact questionnaire
278
+ // before allowing the edit. It is unrelated to peaks-cli, but when the
279
+ // LLM is in a peaks-qa flow and edits `.peaks/_runtime/<sid>/qa/requests/
280
+ // *.md`, the questionnaire demands facts that do not apply (no
281
+ // importers, no public API, no data files, user instruction already
282
+ // in the conversation context). The check below detects the hook in
283
+ // `~/.claude/settings.json` and the project `.claude/settings.json`,
284
+ // and warns when no `.peaks/**` skip is configured.
285
+ //
286
+ // Probing is split out of the check so the check itself stays a pure
287
+ // mapping over `GateguardProbeResult`. Tests inject the probe to keep
288
+ // `~/.claude/settings.json` from leaking into test fixtures.
289
+ // ---------------------------------------------------------------------------
290
+ /** Hook command fragments that identify the gateguard-fact-force hook. */
291
+ const GATEGUARD_HOOK_NEEDLES = ['gateguard', 'fact-force', 'fact_force'];
292
+ /** Token the gateguard hook exposes for "skip these paths" — the check
293
+ * treats any match against `.peaks` (path or globs) as a routed
294
+ * configuration. We accept a few common spellings because the third-
295
+ * party hook's CLI surface is not part of peaks-cli's contract. */
296
+ const GATEGUARD_PEAKS_SKIP_NEEDLES = [
297
+ '.peaks',
298
+ 'peaks-skip',
299
+ 'skip-glob',
300
+ '--skip',
301
+ 'skip_paths'
302
+ ];
303
+ function commandMentionsGateguard(command) {
304
+ if (typeof command !== 'string' || command.length === 0)
305
+ return false;
306
+ const lower = command.toLowerCase();
307
+ return GATEGUARD_HOOK_NEEDLES.some((needle) => lower.includes(needle));
308
+ }
309
+ function entrySkipsPeaks(entry) {
310
+ const matcher = typeof entry.matcher === 'string' ? entry.matcher : '';
311
+ const matcherMentionsPeaks = matcher.toLowerCase().includes('.peaks');
312
+ if (matcherMentionsPeaks)
313
+ return true;
314
+ for (const hook of entry.hooks) {
315
+ const command = typeof hook.command === 'string' ? hook.command : '';
316
+ const lower = command.toLowerCase();
317
+ if (GATEGUARD_PEAKS_SKIP_NEEDLES.some((needle) => lower.includes(needle))) {
318
+ return true;
319
+ }
320
+ }
321
+ return false;
322
+ }
323
+ function extractGateguardEntries(source, sourcePath, settings) {
324
+ if (settings === null || typeof settings !== 'object')
325
+ return [];
326
+ const hooks = settings.hooks;
327
+ if (hooks === null || typeof hooks !== 'object')
328
+ return [];
329
+ const preToolUse = hooks.PreToolUse;
330
+ if (!Array.isArray(preToolUse))
331
+ return [];
332
+ const out = [];
333
+ for (const rawEntry of preToolUse) {
334
+ if (rawEntry === null || typeof rawEntry !== 'object')
335
+ continue;
336
+ const entry = rawEntry;
337
+ if (!Array.isArray(entry.hooks))
338
+ continue;
339
+ const hooks = [];
340
+ for (const rawHook of entry.hooks) {
341
+ if (rawHook === null || typeof rawHook !== 'object')
342
+ continue;
343
+ const h = rawHook;
344
+ const hookEntry = {};
345
+ if (typeof h.type === 'string')
346
+ hookEntry.type = h.type;
347
+ if (typeof h.command === 'string')
348
+ hookEntry.command = h.command;
349
+ hooks.push(hookEntry);
350
+ }
351
+ if (!hooks.some((h) => commandMentionsGateguard(h.command)))
352
+ continue;
353
+ const outEntry = { hooks };
354
+ if (typeof entry.matcher === 'string')
355
+ outEntry.matcher = entry.matcher;
356
+ out.push({ source, sourcePath, entry: outEntry });
357
+ }
358
+ return out;
359
+ }
360
+ function readSettingsJson(path) {
361
+ if (!existsSync(path))
362
+ return null;
363
+ try {
364
+ return JSON.parse(readFileSync(path, 'utf8'));
365
+ }
366
+ catch {
367
+ return null;
368
+ }
369
+ }
370
+ function defaultGateguardProbe() {
371
+ const projectRoot = findProjectRoot(process.cwd());
372
+ const globalPath = join(homedir(), '.claude', 'settings.json');
373
+ const projectPath = projectRoot === null ? null : join(projectRoot, '.claude', 'settings.json');
374
+ return {
375
+ globalSettingsPath: globalPath,
376
+ globalSettings: readSettingsJson(globalPath),
377
+ projectSettingsPath: projectPath,
378
+ projectSettings: projectPath === null ? null : readSettingsJson(projectPath)
379
+ };
380
+ }
381
+ export function collectGateguardEntries(probe) {
382
+ const fromGlobal = extractGateguardEntries('global', probe.globalSettingsPath ?? '~/.claude/settings.json', probe.globalSettings);
383
+ const fromProject = probe.projectSettingsPath === null
384
+ ? []
385
+ : extractGateguardEntries('project', probe.projectSettingsPath, probe.projectSettings);
386
+ return [...fromGlobal, ...fromProject];
387
+ }
273
388
  export async function runDoctor(options = {}) {
274
389
  const checks = [];
275
390
  const registry = await loadSkillRegistry(options.skillsBaseDir);
@@ -619,6 +734,51 @@ export async function runDoctor(options = {}) {
619
734
  message: `Workspace layout check failed: ${getErrorMessage(error)}`
620
735
  });
621
736
  }
737
+ // 2026-06-10 — gateguard-fact-force integration check. The hook is a
738
+ // third-party PreToolUse that fires on Edit / Write and demands a
739
+ // 4-fact questionnaire; when the LLM is in a peaks-qa flow updating
740
+ // `.peaks/_runtime/<sid>/qa/requests/*.md` the facts do not apply.
741
+ // We warn when the hook is installed and no `.peaks/**` skip is
742
+ // configured. The check stays a pure mapping over the probe result
743
+ // so tests can drive it without touching the real `~/.claude/`.
744
+ const gateguardProbe = options.gateguardProbe ?? defaultGateguardProbe;
745
+ try {
746
+ const probe = gateguardProbe();
747
+ const offending = collectGateguardEntries(probe);
748
+ if (offending.length === 0) {
749
+ checks.push({
750
+ id: 'integration:gateguard-peaks-conflict',
751
+ ok: true,
752
+ message: 'No gateguard-fact-force PreToolUse hook detected in ~/.claude/settings.json or project .claude/settings.json; the Edit/Write fact-forcing flow will not interfere with peaks-qa .peaks/ artifact writes'
753
+ });
754
+ }
755
+ else {
756
+ const unrouted = offending.filter((location) => !entrySkipsPeaks(location.entry));
757
+ if (unrouted.length === 0) {
758
+ checks.push({
759
+ id: 'integration:gateguard-peaks-conflict',
760
+ ok: true,
761
+ message: `gateguard-fact-force hook is installed in ${offending.map((l) => l.source).join(' + ')} but a .peaks/** skip pattern is configured; peaks-qa .peaks/ artifact writes are not blocked`
762
+ });
763
+ }
764
+ else {
765
+ const sources = Array.from(new Set(unrouted.map((u) => u.sourcePath))).join(' + ');
766
+ const matchers = unrouted.map((u) => u.entry.matcher ?? '*').join(', ');
767
+ checks.push({
768
+ id: 'integration:gateguard-peaks-conflict',
769
+ ok: false,
770
+ message: `gateguard-fact-force PreToolUse hook is installed (${sources}, matcher: ${matchers}) with no .peaks/** skip pattern; every Edit/Write of a peaks-qa envelope (.peaks/_runtime/<sid>/qa/requests/*.md) will be intercepted and demand a 4-fact questionnaire that does not apply to QA templates. Workaround: set \`ECC_DISABLED_HOOKS=pre:edit-write:gateguard-fact-force\` for the session, OR add a paired PreToolUse entry whose matcher restricts the hook to non-.peaks paths. peaks-cli is NOT the source of this hook.`
771
+ });
772
+ }
773
+ }
774
+ }
775
+ catch (error) {
776
+ checks.push({
777
+ id: 'integration:gateguard-peaks-conflict',
778
+ ok: true,
779
+ message: `gateguard probe failed (${getErrorMessage(error)}); skipping check`
780
+ });
781
+ }
622
782
  try {
623
783
  const schemaText = await readText(join(schemaRoot, 'doctor-report.schema.json'));
624
784
  const schema = JSON.parse(schemaText);
@@ -0,0 +1,16 @@
1
+ export type DetectPresenceMarkerInput = {
2
+ project: string;
3
+ latestAssistantMessage: string;
4
+ };
5
+ export type DetectPresenceMarkerResult = {
6
+ active: boolean;
7
+ skill?: string;
8
+ markerFound: boolean;
9
+ warning?: string;
10
+ };
11
+ export type PresenceMarkerWarning = (typeof PRESENCE_MARKER_WARNING)[number];
12
+ export declare const PRESENCE_MARKER_WARNING: readonly ["Peaks skill context may have been lost from this conversation; please re-invoke /peaks-<skill>."];
13
+ /**
14
+ * Pure read-only presence-marker detection. No I/O side effects.
15
+ */
16
+ export declare function detectPresenceMarker(input: DetectPresenceMarkerInput): DetectPresenceMarkerResult;
@@ -0,0 +1,105 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ /**
4
+ * Slice 028 (Q1=A): hook-based skill-presence marker detection.
5
+ *
6
+ * Background: the consumer-facing CLAUDE.md template (rendered by
7
+ * `peaks standards init` / `peaks standards update`) instructs the LLM
8
+ * to display a compact status header
9
+ * `Peaks-Cli Skill: <skill> | Peaks-Cli Gate: <gate> | Next: <one short action>`
10
+ * on every turn while a peaks skill is active. If the LLM forgets (e.g.
11
+ * because of context compaction or a fresh session), the user is left
12
+ * without an at-a-glance signal that peaks is orchestrating the work.
13
+ *
14
+ * This service is the read-only side of the slice-028 detection
15
+ * mechanism. The PostToolUse hook (or any other consumer, e.g.
16
+ * `peaks skill detect-marker-loss`) calls
17
+ * `detectPresenceMarker({ project, latestAssistantMessage })`
18
+ * and gets back:
19
+ *
20
+ * - `active`: whether an active-skill marker was found on disk.
21
+ * - `skill?`: the active skill name, if any.
22
+ * - `markerFound`: whether the latest assistant message carries the
23
+ * expected `Peaks-Cli Skill:` / `Peaks-Cli Gate:`
24
+ * marker. Always `false` when `active` is `false`.
25
+ * - `warning?`: a human-readable warning emitted when the marker
26
+ * is missing while the presence is active.
27
+ *
28
+ * The function is pure: it does not write to disk, does not clear the
29
+ * presence file, and does not depend on `process.cwd()`. The caller is
30
+ * expected to provide the absolute project root (peaks-cli convention
31
+ * from the standards-commands family — see dev-preference rule
32
+ * `project-option-is-canonical-project-root-source`).
33
+ */
34
+ const PRESENCE_CANONICAL_PATH = '.peaks/_runtime/active-skill.json';
35
+ const PRESENCE_LEGACY_PATH = '.peaks/.active-skill.json';
36
+ const MARKER_PRIMARY = 'Peaks-Cli Skill:';
37
+ const MARKER_SECONDARY = 'Peaks-Cli Gate:';
38
+ const SKILL_NAME_RE = /"skill"\s*:\s*"([^"\\]*(?:\\.[^"\\]*)*)"/;
39
+ export const PRESENCE_MARKER_WARNING = [
40
+ 'Peaks skill context may have been lost from this conversation; please re-invoke /peaks-<skill>.'
41
+ ];
42
+ function readPresenceFile(absolutePath) {
43
+ if (!existsSync(absolutePath))
44
+ return null;
45
+ let raw;
46
+ try {
47
+ raw = readFileSync(absolutePath, 'utf8');
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ let parsed;
53
+ try {
54
+ parsed = JSON.parse(raw);
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ if (parsed === null || typeof parsed !== 'object')
60
+ return null;
61
+ const skillMatch = SKILL_NAME_RE.exec(JSON.stringify(parsed));
62
+ if (skillMatch === null)
63
+ return null;
64
+ if (typeof skillMatch[1] !== 'string' || skillMatch[1].length === 0)
65
+ return null;
66
+ return { skill: skillMatch[1] };
67
+ }
68
+ function readPresenceBackCompat(project) {
69
+ const projectRoot = resolve(project);
70
+ const canonicalPath = resolve(projectRoot, PRESENCE_CANONICAL_PATH);
71
+ const legacyPath = resolve(projectRoot, PRESENCE_LEGACY_PATH);
72
+ for (const candidate of [canonicalPath, legacyPath]) {
73
+ const parsed = readPresenceFile(candidate);
74
+ if (parsed === null)
75
+ continue;
76
+ return { skill: parsed.skill, path: candidate };
77
+ }
78
+ return null;
79
+ }
80
+ function messageHasMarker(message) {
81
+ if (message.length === 0)
82
+ return false;
83
+ return message.includes(MARKER_PRIMARY) || message.includes(MARKER_SECONDARY);
84
+ }
85
+ /**
86
+ * Pure read-only presence-marker detection. No I/O side effects.
87
+ */
88
+ export function detectPresenceMarker(input) {
89
+ const project = input.project;
90
+ const message = input.latestAssistantMessage ?? '';
91
+ const presence = readPresenceBackCompat(project);
92
+ if (presence === null) {
93
+ return { active: false, markerFound: false };
94
+ }
95
+ const markerFound = messageHasMarker(message);
96
+ if (markerFound) {
97
+ return { active: true, skill: presence.skill, markerFound: true };
98
+ }
99
+ return {
100
+ active: true,
101
+ skill: presence.skill,
102
+ markerFound: false,
103
+ warning: PRESENCE_MARKER_WARNING[0]
104
+ };
105
+ }
@@ -142,4 +142,23 @@ export declare function summarizeProjectMemoryBackupResult(result: ProjectMemory
142
142
  */
143
143
  export declare function ensureMemoryBootstrap(projectRoot: string): boolean;
144
144
  export declare function readProjectMemories(projectRoot: string): ProjectMemoryReadResult;
145
+ export interface ProjectMemoryShowResult {
146
+ projectRoot: string;
147
+ memoryDir: string;
148
+ name: string;
149
+ body: string;
150
+ filePath: string;
151
+ updatedAt: string | null;
152
+ kind: ProjectMemoryKind | null;
153
+ title: string;
154
+ /** Whether the on-disk body bytes are returned (true) or a compact form (false). */
155
+ pretty: boolean;
156
+ }
157
+ /**
158
+ * Read a single project memory's full body by name. Returns null when
159
+ * the memory does not exist. The on-disk body is returned verbatim
160
+ * (pretty). The CLI layer applies `formatMdCompact` when `format: 'compact'`
161
+ * is requested. Slice 023 (R3).
162
+ */
163
+ export declare function readProjectMemoryBody(projectRoot: string, name: string): ProjectMemoryShowResult | null;
145
164
  export {};
@@ -779,3 +779,36 @@ export function readProjectMemories(projectRoot) {
779
779
  memories
780
780
  };
781
781
  }
782
+ /**
783
+ * Read a single project memory's full body by name. Returns null when
784
+ * the memory does not exist. The on-disk body is returned verbatim
785
+ * (pretty). The CLI layer applies `formatMdCompact` when `format: 'compact'`
786
+ * is requested. Slice 023 (R3).
787
+ */
788
+ export function readProjectMemoryBody(projectRoot, name) {
789
+ const normalizedRoot = normalizeRoot(projectRoot);
790
+ const memoryDir = assertSafeProjectMemoryDir(normalizedRoot);
791
+ if (!existsSync(memoryDir)) {
792
+ ensureMemoryBootstrap(normalizedRoot);
793
+ }
794
+ for (const filePath of listMarkdownFiles(memoryDir)) {
795
+ if (basename(filePath, '.md') !== name)
796
+ continue;
797
+ const parsed = parseStoredMemoryFile(readFileSync(filePath, 'utf8'), filePath);
798
+ if (parsed === null)
799
+ continue;
800
+ const updatedAt = readMemoryFileMtime(filePath);
801
+ return {
802
+ projectRoot: normalizedRoot,
803
+ memoryDir,
804
+ name: parsed.name,
805
+ body: parsed.body,
806
+ filePath,
807
+ updatedAt,
808
+ kind: parsed.kind,
809
+ title: parsed.title,
810
+ pretty: true
811
+ };
812
+ }
813
+ return null;
814
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * migrate-from-md — one-time migration from `.peaks/retrospective/<id>/*.md`
3
+ * per-workflow MD dirs to a single `.peaks/retrospective/index.json` plus a
4
+ * `.peaks/_archive/retrospective-2026-06-09-pre-r3.tar.gz` archive.
5
+ *
6
+ * Slice 023 (R3) G9. Idempotent: re-run is a no-op when `index.json` has
7
+ * 88 entries with matching `updatedAt`.
8
+ *
9
+ * The legacy MDs in this repo use a *bullet-list* metadata format (no YAML
10
+ * frontmatter). The fields we need are extracted from the leading bullet
11
+ * block: `session:`, `rid:`, `type:`, `sliceId:`, plus the first `# Title`
12
+ * heading.
13
+ */
14
+ export interface MigrateOptions {
15
+ projectRoot: string;
16
+ apply?: boolean;
17
+ includeFailed?: boolean;
18
+ expectedEntries?: number;
19
+ }
20
+ export interface MigrateResult {
21
+ apply: boolean;
22
+ projectRoot: string;
23
+ indexPath: string;
24
+ archivePath: string | null;
25
+ sourceDir: string;
26
+ totalLegacyDirs: number;
27
+ totalLegacyMds: number;
28
+ parsedEntries: number;
29
+ failedEntries: Array<{
30
+ id: string;
31
+ reason: string;
32
+ }>;
33
+ archiveVerified: boolean;
34
+ status: 'applied' | 'no-op' | 'partial' | 'failed';
35
+ warnings: string[];
36
+ }
37
+ export declare function migrateRetrospectiveFromMd(options: MigrateOptions): MigrateResult;