import type { EccComponentId, EccComponentSelection, EccMcpComponentId } from "./components.js"; export interface EccComponentInstallDescriptor { evidenceComponentId: string; containingModuleId: string; wholeModules?: readonly string[]; skills?: readonly string[]; agents?: readonly string[]; sourceRoots?: readonly string[]; agentScaffolding?: boolean; } export interface EccManifestOperation { kind: string; moduleId: string; sourceRelativePath: string; destinationPath: string; } export interface EccManifestPlan { operations: Operation[]; statePreview: { operations: Operation[]; }; } export interface EccMaterializationSpec { scope: "scoped" | "full"; moduleIds: string[]; wholeModules: string[]; skills: string[]; agents: string[]; sourceRoots: string[]; agentScaffolding: boolean; /** * Governance leaves ECC's agents, skills, rules, and commands intact, but * AIH is the sole owner of MCP and host-hook/runtime configuration. The * verified upstream materializer receives this explicit operation filter. */ excludeAihOwnedSurfaces?: boolean; } export declare function eccComponentInstallDescriptor(componentId: EccComponentId | EccMcpComponentId): EccComponentInstallDescriptor; export declare function eccMaterializationSpec(selection: EccComponentSelection): EccMaterializationSpec; export declare function eccComponentSourcePaths(componentId: EccComponentId | EccMcpComponentId): string[]; export declare function eccManifestOperationSelected(operation: EccManifestOperation, selection: EccComponentSelection): boolean; export declare function filterEccManifestPlan(plan: EccManifestPlan, selection: EccComponentSelection): void; export type GovernedEccOperationClass = "ecc-content" | "mcp" | "host-runtime"; /** Runtime roots used to bind a verified upstream destination to this install. */ export interface GovernedEccDestinationRoots { projectRoot: string; homeDir: string; target: string; } /** * Each retained upstream content source has exactly one owned target path. * Matching a suffix somewhere below an otherwise trusted root is not enough: * it could remap a rule to a skill or overwrite a sibling's content. * * Exported because it is the single answer to "where does this component's * source file land for this target". A target adapter that needed the same * answer and restated it would be a second mapping able to drift from the one * the governed classifier below enforces. * * Three rows are target-independent and therefore SHARED by every target: * `AGENTS.md`, `.agents/plugins/` and `.agents/skills/` are the same * destination whoever asks. `.claude/commands/` is the opposite — a surface * Claude owns exclusively — so it answers for the Claude target and for nobody * else: a non-Claude install that landed there would write another tool's * directory, and the callers of this mapping fail closed on an unmapped source * rather than inventing a destination for it. * * One target's project root is not `.`: Kimi's is `.kimi-code`, which * is where the framework's own Kimi adapter roots a project install and keeps * its install state. `.kimi` is that framework's OBSOLETE compatibility-docs * directory — its adapter deliberately does not recreate it — so deriving the * root would write a directory upstream abandoned. Overriding here rather than * at a call site keeps this the single answer to "where does this source land * for this target", which is what the governed classifier enforces against. * * One target has NO generic rows at all. OpenCode's only framework adapter is * home-scoped (`TARGET_LOCATIONS` in `reconcile.ts` records it as * `{ scope: "home", rootSegment: ".opencode" }`) and the pinned install-preview * artifact carries, for OpenCode, home config merges and nothing project-scoped * — so no evidence says where a per-component OpenCode project layout would go. * The four generic rows are therefore suppressed for it and OpenCode ships the * shared target-independent rows only. Deriving `.opencode/agents/` from the * parameterized root would invent a directory nothing is known to read, and * this function is the single answer to where a source lands: an invented * answer here becomes an invented answer in the receipt, in the classifier, and * in what uninstall then removes. A small target with real, named refusals is * the honest shape; a full-looking one built on a guess is not. */ export declare function eccContentDestinationMapping(source: string, target: string | undefined): { scope: "project" | "home"; relative: string; } | undefined; /** * Classify an operation from the upstream manifest rather than from an ECC * profile/module. Core and platform modules are intentionally mixed: their * module id is useful identity evidence, but it cannot decide ownership. * * A merge into an unrecognized runtime path is never guessed to be harmless. * New upstream operation forms therefore stop a governed install before apply. */ export declare function classifyGovernedEccOperation(operation: EccManifestOperation, roots?: GovernedEccDestinationRoots): GovernedEccOperationClass; /** * Apply normal component selection first, then remove only the explicitly * classified AIH-owned surfaces. This is intentionally operation-level even * for Core/platform and full scope, whose modules contain mixed ownership. */ export declare function filterGovernedEccManifestPlan(plan: EccManifestPlan, selection: EccComponentSelection, roots?: GovernedEccDestinationRoots): void;