/** * Bundle file collection + secret scanning (design: docs/designs/clustly-cli.md §5/§5c; * build-plan slice 2.4). The scanner runs over the files that would actually ship (i.e. after * manifest excludes), and every hit is a HARD refusal with a fix-it hint — secrets travel via * `clustly secrets set`, never in the bundle. The walker is shared with packing (slice 2.6). */ import type { ClustlyManifest } from "./manifest"; export interface ScanFinding { /** Repo-relative path inside the workspace. */ path: string; message: string; /** How to make it go away — shown as the fix-it list. */ fix: string; } export interface CollectedFiles { /** Workspace-relative paths of everything that would ship, sorted. */ files: string[]; /** Symlinks that escape the workspace — always Block-level findings (design §5c.2). */ escapingSymlinks: string[]; } /** * Walk the workspace applying the manifest's excludes (name-based, any depth). `include` is * honored at packing (slice 2.6); the scanner deliberately sees everything not excluded. */ /** Tar entry names and manifest patterns are `/`-separated on every platform (Windows too). */ export declare const toBundlePath: (rel: string) => string; export declare function collectBundleFiles(workspace: string, manifest: ClustlyManifest): CollectedFiles; /** Files above this are skipped by content rules. Shared with inventory.ts — one policy, one name. */ export declare const MAX_CONTENT_SCAN_BYTES: number; /** NUL-sniff window for binary detection. Shared with inventory.ts. */ export declare const BINARY_SNIFF_BYTES = 8192; /** NUL in the sniff window → binary; content rules don't apply. */ export declare function isBinary(buf: Buffer): boolean; /** Scan the shippable set. Every finding is Block-level: the deploy refuses until the list is empty. */ export declare function scanForSecrets(workspace: string, collected: CollectedFiles): ScanFinding[];