import type { PortableSkillManifest } from "./portable-skills-types.js"; import type { SkillBundleEntry } from "./skill-bundle.js"; /** * Canonical content hashing for the hasna.skill.v1 portable bundle. * * The hash covers the normalized skill bundle: skill.json (blank-canonicalized — * its own `content_hash` field removed, keys sorted, line endings normalized) * plus SKILL.md and every file under src/, scripts/, assets/, and references/, * plus AGENTS.md, package.json, and tsconfig.json. Entries are sorted by * relative path and hash-stable across platforms (posix separators, LF * endings, sorted keys), so two checkouts of the same skill produce the same * hash regardless of OS or editor. * * Excluded, mirroring the port/copy rules: node_modules, .git, dist, build, * .turbo, dot-entries, and symlinks (symlinks are validation errors anyway). */ export declare const CONTENT_HASH_ALGORITHM = "sha256"; export declare const CONTENT_HASH_HEX_LENGTH = 64; /** Normalize CRLF/CR to LF for hash stability across platforms. */ export declare function normalizeLineEndings(content: string): string; /** * Blank-canonicalize a skill.json manifest for hashing: parse it, drop the * self-referencing `content_hash` field, and re-serialize with sorted keys so * key order and the hash's own presence never change the digest. */ export declare function canonicalizeManifest(raw: string): string; export interface BundleFile { /** Posix relative path from the skill root, e.g. `scripts/setup.sh`. */ rel: string; /** Normalized text content for text files; raw buffer for binary files. */ content: Uint8Array; } /** * Collect the normalized bundle files for a skill folder, sorted by relative * path. Only the documented coverage set is included. */ export declare function collectBundleFiles(skillPath: string): BundleFile[]; /** * Compute the canonical content hash of a skill folder. Stable across * platforms: sorted posix paths, LF line endings, canonicalized manifest. */ export declare function computeContentHash(skillPath: string): string; export interface ContentHashVerification { /** Whether the skill declares a content_hash at all. */ declared: boolean; /** Whether the declared hash is present, well-formed, and matches the bundle. */ valid: boolean; /** The declared value, if any. */ declaredHash?: string; /** The recomputed hash over the current bundle. */ computedHash?: string; } export interface ContentHashLimits { entries: number; rawBytes: number; normalizedBytes: number; fileBytes: number; normalizedFileBytes: number; pathBytes: number; manifestBytes: number; manifestDepth: number; timeoutMs: number; } /** Hard ceilings for the entry API. Callers may tighten these, never disable them. */ export declare const CONTENT_HASH_LIMITS: Readonly; export interface ContentHashOptions { limits?: Partial; signal?: AbortSignal; } export type ContentHashInputErrorCode = "CONTENT_HASH_INVALID" | "CONTENT_HASH_LIMIT" | "CONTENT_HASH_ABORTED" | "CONTENT_HASH_TIMEOUT"; export declare class ContentHashInputError extends Error { readonly code: ContentHashInputErrorCode; constructor(code: ContentHashInputErrorCode, message: string); } /** * Hash bounded ordinary regular-file entries without filesystem access or extraction. * Entries are revalidated and copied, even if supplied by inspectSkillBundle. This is * content identity, not manifest validity, permission to execute, or a JS sandbox. * Text uses the directory oracle's nonfatal UTF-8/LF normalization. Excluded paths * still count toward input limits and collision checks. Inputs are never mutated. */ export declare function computeContentHashFromEntries(entries: readonly SkillBundleEntry[], options?: ContentHashOptions): Promise; /** Verify only the declaration in the same owned skill.json, never a second manifest. */ export declare function verifyContentHashFromEntries(entries: readonly SkillBundleEntry[], options?: ContentHashOptions): Promise; /** * Verify a skill folder's declared content_hash against the recomputed * canonical hash of the current bundle. A missing declaration or a mismatch * fails; a malformed declaration fails. */ export declare function verifyContentHash(skillPath: string, manifest?: PortableSkillManifest): ContentHashVerification; /** Path sanity helper used by the contract validator: the skill folder exists. */ export declare function skillFolderExists(skillPath: string): boolean; /** * Canonical SKILL.md document hash for home-vs-corpus comparison. * * The bundle-wide hash above covers a whole skill folder; agent home * directories carry only a SKILL.md, so the home comparison hashes exactly * that one document. The normalization is the canonical one: LF line endings * (LF-invariant, so the same document hashes identically across platforms), * with the single agent-adaptation delta removed — `user_invocable` frontmatter * lines, which `sync` injects for Claude and strips for every other agent. * Two copies that differ only in those two ways hash identically, so an * unmarked home copy that matches the canonical SKILL.md modulo adaptation is * recognised as an exact match. */ export declare function canonicalAgentSkillMarkdown(content: string): string; /** Canonical hash of a SKILL.md document (modulo LF endings and user_invocable). */ export declare function hashSkillMarkdown(content: string): string; /** Canonical hash of the SKILL.md at `path`. */ export declare function hashSkillMarkdownFile(path: string): string;