/** * rid `2026-09-13-a2-post-compact-reinject` — * `peaks session reinject --project `. * * The transport for `buildPostCompactReinjectionCard`. A Claude Code * `SessionStart` hook (installed by `peaks hooks install`, matcher `compact`) * runs this and Claude Code adds its stdout to the context — which is the * whole mechanism by which the engineering state comes back after a * compaction. See `src/services/context/post-compact-reinjection.ts` for what * the card says and why it has a byte budget. * * THE ONE THING THIS FILE HAS TO GET RIGHT * * stdout IS context here. Every other peaks command treats stdout as a * channel for a human or a JSON consumer; this one's stdout is pasted in * front of the model as though it were something peaks-loop meant to say. * Two consequences, and both are the reason the code below looks the way it * does: * * 1. The card is printed VERBATIM — no `printResult`, no envelope, no * `next:` lines, no JSON on the card path. Anything else would be * injected too, and a `{"ok":true,...}` blob in the context is noise the * model then has to interpret. * 2. A FAILURE PRINTS NOTHING. Not an error, not a warning, not a hint. An * error message on stdout would be injected into the context as a fact, * and a model that reads `REINJECT_FAILED: project root must be * canonical` in its own context has been handed a false problem to solve * mid-slice. So every failure path exits 0 with empty stdout, and the * reason is reported only through `--json` (which the hook never passes). * This is `failure-soft` for a SessionStart hook: the session must start * whether or not there is any state to re-inject. * * `--json` exists for the human/diagnostic path — `peaks session reinject * --json` is how you inspect what the hook would inject without reading a * transcript. It is deliberately NOT what the hook runs. */ import type { Command } from 'commander'; import type { ProgramIO } from '../cli-helpers.js'; import { buildPostCompactReinjectionCard } from '../../services/context/post-compact-reinjection.js'; export type ReinjectOptions = { project: string; json?: boolean; budgetBytes?: string; }; /** Why no card was produced. Reported through `--json` only. */ export type ReinjectSkipReason = 'empty-project' | 'invalid-project-root' | 'unreadable-project-root'; export type ReinjectResult = { readonly ok: true; readonly card: ReturnType; } | { readonly ok: false; readonly reason: ReinjectSkipReason; readonly message: string; }; /** * Pure-ish action body, exported so tests can drive it with inputs Commander * would mangle (trailing NUL bytes, whitespace-only paths). Never throws and * never writes to stdout itself — `runReinjectAction` is the IO edge. */ export declare function buildReinjectResult(opts: ReinjectOptions): ReinjectResult; /** * The IO edge. Returns the process exit code so callers (CLI path or test * path) apply it uniformly. NEVER calls `process.exit`. * * Both branches return 0 on the failure path — see this file's header. The * only way a caller sees `exitCode: 1` is a bug in the `--json` serializer * itself, and even that is caught. */ export declare function runReinjectAction(opts: ReinjectOptions, io: ProgramIO): { exitCode: 0 | 1; }; export declare function registerReinjectCommand(program: Command, io: ProgramIO): void;