import type { ChangesetEntry, ChangesetKind } from '@cleocode/contracts'; /** * Provenance label for an aggregated entry — distinguishes whether the bytes * came from the canonical docs SSoT blob store or from a `.changeset/*.md` * file that has not yet been mirrored to SSoT. * * Surfaces under `meta.source` on every {@link AggregatedChangesetEntry}. * * @task T9793 */ export type ChangesetSource = 'ssot' | 'file'; /** * One aggregated changeset entry paired with its provenance. * * Returned by {@link readChangesetsSsotFirst} — wraps the parsed * {@link ChangesetEntry} with the source label so downstream consumers * (release-notes composer, debugging surfaces) can tell at a glance whether * a given entry was sourced from SSoT or a legacy `.changeset/*.md` file. * * @task T9793 */ export interface AggregatedChangesetEntry { /** The parsed changeset entry. */ readonly entry: ChangesetEntry; /** Provenance label — `'ssot'` when read from blob store, `'file'` otherwise. */ readonly meta: { readonly source: ChangesetSource; }; } /** * Result of aggregating a slice of changeset entries into a release CHANGELOG * section. The `markdown` field is empty string when `entries.length === 0` so * callers can compose conditionally without branching on the contents. */ export interface AggregatedChangesetSection { /** Rendered CHANGELOG markdown section. Empty string when no entries. */ readonly markdown: string; /** Number of entries rolled into the section. */ readonly entryCount: number; /** Distinct kinds represented in the rendered section. */ readonly kinds: ReadonlySet; } /** * Options accepted by {@link aggregateChangesetsForRelease}. */ export interface AggregateChangesetsOptions { /** Parsed changeset entries to aggregate. */ readonly entries: readonly ChangesetEntry[]; /** Release version string (e.g. `v2026.6.0`). Used in the section header. */ readonly version: string; /** ISO-8601 calendar date (YYYY-MM-DD). Used in the section header. */ readonly date: string; /** Optional human-readable release title appended after the version header. */ readonly title?: string; } /** * Aggregate a parsed set of {@link ChangesetEntry} records into a single * CHANGELOG section. * * Section layout: * * ```md * ## [— ] * * ### BREAKING CHANGES ← only when at least one breaking entry exists * * - <summary> (T####) (#PR) * * Migration: * * <breaking note indented> * * ### Features * * - <summary> (T####) (#PR) * ``` * * Empty input → returns `{ markdown: '', entryCount: 0, kinds: new Set() }` so * the caller can skip emission without branching on string length. * * @example * ```ts * import { parseChangesetDir } from '@cleocode/core/changesets'; * import { aggregateChangesetsForRelease } from '@cleocode/core/release/changesets-aggregator'; * * const entries = parseChangesetDir('.changeset'); * const section = aggregateChangesetsForRelease({ * entries, * version: 'v2026.6.0', * date: '2026-05-20', * }); * console.log(section.markdown); * ``` */ export declare function aggregateChangesetsForRelease(opts: AggregateChangesetsOptions): AggregatedChangesetSection; /** * Read every changeset entry available in the project, preferring SSoT bytes * when present and falling back to `.changeset/*.md` for slugs that have not * yet been mirrored to SSoT. * * Algorithm: * 1. Query the attachment store for every row where `type='changeset'`. * Each row carries the slug + content sha256 — the canonical address. * 2. Read `.changeset/*.md` from disk for the same project. * 3. Build a slug→source map: SSoT wins on slug-match (sha-dedup happens * automatically because both surfaces hash the same canonical bytes). * 4. For each unique slug, parse the bytes (from SSoT) or the file (from * disk), attach a `meta.source` label, and return them. * * Entries that fail validation in EITHER surface are silently skipped — * callers receive only the validated subset. The aggregator is advisory * metadata and one malformed row must not block release planning. * * @param projectRoot - Absolute path to the project root. * @returns Aggregated entries sorted by id (deterministic for downstream * diff-friendliness). SSoT-first; file-fallback for unmirrored slugs. * * @example * ```ts * const entries = await readChangesetsSsotFirst('/path/to/repo'); * for (const { entry, meta } of entries) { * console.log(`${entry.id} ← ${meta.source}`); * } * ``` * * @task T9793 */ export declare function readChangesetsSsotFirst(projectRoot: string): Promise<AggregatedChangesetEntry[]>; /** * Convenience wrapper — strip provenance and return just the entries, for * callers (like `cleo release plan`) that feed the result straight into * {@link aggregateChangesetsForRelease}. * * Reads the entries via {@link readChangesetsSsotFirst} then maps to the * underlying `ChangesetEntry[]`. Loses the `meta.source` label — use the * full reader when provenance is needed. * * @param projectRoot - Absolute path to the project root. * @returns Validated entries, SSoT-first. * * @task T9793 */ export declare function readChangesetEntriesSsotFirst(projectRoot: string): Promise<ChangesetEntry[]>; /** * Synchronous helper for {@link readChangesetEntries} in `plan.ts` — * the file-only fallback used when `readChangesetEntriesSsotFirst` is not * yet wired into a code path that can `await`. * * Mirrors the legacy behaviour of `parseChangesetDir(.changeset)` — kept * because the release plan pipeline (T9753) calls it synchronously from * `releasePlan()` and the broader async refactor is out of scope here. * * The async {@link readChangesetEntriesSsotFirst} is the preferred surface * for new code. * * @internal * @task T9793 */ export declare function readChangesetEntriesFileOnly(projectRoot: string): ChangesetEntry[]; /** * Probe the existence of a `.changeset/<slug>.md` file. Test-helper safety * net used by the unit tests — exposed because the writer's file-write step * uses `tmp-then-rename` which can race in tight test loops. * * @internal */ export declare function changesetFileExists(projectRoot: string, slug: string): boolean; /** * Read a `.changeset/<slug>.md` file's raw bytes. Test-helper used by the * aggregator-ssot-first test to verify provenance labels match expectations. * * @internal */ export declare function readChangesetFileBytes(projectRoot: string, slug: string): Buffer | null; //# sourceMappingURL=changesets-aggregator.d.ts.map