/** * `scai hygiene explain why-blocked ` — answer the question "why * won't this item delete?" with a structured, kind-sorted list of * every inbound reference that would block (or potentially block) a * Authoring-API delete. * * The Authoring API rejects delete requests with terse messages like * "Template is used by at least one item" — enough to know something * is wrong, not enough to know what. Operators (and agents) end up * piecing together the picture by hand: run `audit references`, then * `audit template-dependencies`, then cross-reference the field * names. This verb does that composition once and returns a single * report with each blocker labeled by reference kind. * * Combines two existing primitives: * * - `audit references` (text walk of content fields) * - `audit template-dependencies` (search-index queries against the * five structural fields `_template` / `_basetemplates` / * `__masters` / `__source` / `datasource template`) * * Both are invoked with `silent: true` so this verb owns the printed * report. */ import { type ReferenceKind } from "../reference-kind.js"; import { type HygieneCommonOptions } from "../shared.js"; export interface ExplainWhyBlockedOptions extends HygieneCommonOptions { /** Required. ItemId to explain (any GUID form accepted). */ itemId?: string; /** Content root for the field-value scan. Default `/sitecore/content`. */ root?: string; /** Override the search index. */ index?: string; /** Cap on inbound refs returned per check. Default 5000. */ limit?: number; /** * Skip the field-value content scan (the slow part). Use when you * already know the target is a template and only structural refs * matter — typically when triaging a template-delete failure. */ skipContentScan?: boolean; /** * Skip the template-dependencies (search-index) check. Use when * the target is a content item that isn't referenced as a template * by anything — saves the five search-index round-trips. Most useful * for triaging deletes of leaf content items, never for templates. */ skipTemplateDeps?: boolean; /** Pass through to the field-value scan when caching back-to-back invocations. */ cache?: boolean; /** * Suppress the printed report. The structured `ExplainWhyBlockedReport` * is still returned — set by non-CLI callers (the MCP `explain` tool) * that render the result themselves and must not write to stdout. */ silent?: boolean; } export interface BlockerEntry { referenceKind: ReferenceKind; referrerItemId: string; referrerPath: string | null; /** Field name that holds the ref. Null for template-deps results that don't surface a field. */ fieldName: string | null; /** Templating context — `null` when the referrer isn't a template. */ referrerTemplateName: string | null; /** Source of the finding — useful when both scans surface the same referrer for different reasons. */ source: "audit-references" | "audit-template-dependencies"; } export interface ExplainWhyBlockedReport { /** Normalized 32-char itemId of the target. */ itemId: string; blockers: BlockerEntry[]; } /** * Run both audits in parallel against `itemId`, merge the findings * into a single sorted-by-kind blocker list, and print the canonical * envelope. Used by `scai hygiene explain why-blocked ` and any * automation that wants a single answer to "what's holding this * delete back?" */ export declare const runExplainWhyBlocked: (options: ExplainWhyBlockedOptions) => Promise;