/** * Pure path-string helpers. * * **This module's only import is `node:path`.** Nothing here touches the * filesystem, the OS, or URLs — that is the whole point: the `./path` and * `./glob` subpath entries re-export from here so importing them can never * pull `node:fs`, `node:os`, or `node:url` into a consumer's graph. * * Filesystem-touching path helpers (`normalizePath`, `normalizedTmpdir`, * `mkdirSyncReal`, `resolveFromImportMeta`, `dynamicImportPath`) live in * `./path-utils.ts` and are exposed via the `./fs` entry. */ /** * Check if a path is absolute * * Cross-platform detection of absolute paths: * - Unix: /path/to/file * - Windows: C:\path\to\file or C:/path/to/file * * @param p - Path to check * @returns True if path is absolute * * @example * isAbsolutePath('/path/to/file') // true * isAbsolutePath('./relative') // false * isAbsolutePath('C:/Windows') // true (Windows) */ export declare function isAbsolutePath(p: string): boolean; /** * True if `p` is absolute on ANY platform — a POSIX root path (`/etc`), a * Windows drive-letter path (`C:\…` or `C:/…`), or a UNC path (`\\host\share`). * * Unlike {@link isAbsolutePath} (host-platform only), this is host-independent, * so config-containment checks reject Windows-absolute paths even when run on * POSIX CI, and vice versa. Used to keep config-supplied relative paths (skill * `files:` dest) from escaping their anchor directory (zip-slip class). * * @example * isAbsoluteAnyPlatform('/etc/passwd') // true (POSIX) * isAbsoluteAnyPlatform('C:\\Users') // true (Windows drive) * isAbsoluteAnyPlatform('scripts/cli') // false (relative) */ export declare function isAbsoluteAnyPlatform(p: string): boolean; /** * True if `p` contains a `..` parent-directory traversal segment. * * Forward-slash-normalized, then inspects each `/`-delimited segment — so a * `..` is caught regardless of the original OS separator. A containment guard * for config-supplied relative paths (skill `files:` dest values, glob magic * remainders) that must never climb above their anchor directory. * * @example * hasParentTraversalSegment('a/../b') // true * hasParentTraversalSegment('a/b/c') // false * hasParentTraversalSegment('..\\evil') // true (backslash normalized) * hasParentTraversalSegment('a..b/c') // false (".." must be a whole segment) */ export declare function hasParentTraversalSegment(p: string): boolean; /** * Compute a `ValidationIssue.location`: an absolute source file path made * relative to the scan/project root, forward-slashed. * * This is the ONE relativizer every VAT validation lane uses. `location` is * contractually project-relative (see `ValidationIssue` in * `@vibe-agent-toolkit/agent-schema`), so producers must route through here * rather than emitting `skillPath` directly — absolute locations leak the * developer's home directory into CI logs and make `validation.allow` globs, * which match against `location`, unwritable. * * `projectRoot` is required precisely because "relative to what?" has no safe * default: a caller with no root must decide one (the skill directory, the * scan root) rather than silently falling back to an absolute path. * * @param sourceFilePath - Absolute path to the file the issue was found in. * @param projectRoot - Root the location is expressed relative to. * @returns Forward-slashed relative location. * * @example * issueLocation('/repo/skills/foo/SKILL.md', '/repo') // 'skills/foo/SKILL.md' */ export declare function issueLocation(sourceFilePath: string, projectRoot: string): string; /** * Convert a relative path to absolute * * If path is already absolute, returns it normalized. * Otherwise resolves relative to baseDir. * * @param p - Path to convert * @param baseDir - Base directory for resolution * @returns Absolute path with **forward slashes** (cross-platform safe) * * @example * toAbsolutePath('./docs/README.md', '/project') * // Returns: '/project/docs/README.md' * * toAbsolutePath('/absolute/path.md', '/project') * // Returns: '/absolute/path.md' */ export declare function toAbsolutePath(p: string, baseDir: string): string; /** * Get the relative path from one file to another * * Useful for generating relative links between markdown files. * * @param from - Source file path (absolute) * @param to - Target file path (absolute) * @returns Relative path from source to target with **forward slashes** (cross-platform safe) * * @example * getRelativePath('/project/docs/guide.md', '/project/README.md') * // Returns: '../README.md' * * getRelativePath('/project/README.md', '/project/docs/api.md') * // Returns: 'docs/api.md' */ export declare function getRelativePath(from: string, to: string): string; /** * Convert a path to forward slashes * * Windows accepts both forward slashes and backslashes as path separators. * This function normalizes all paths to use forward slashes for consistency. * Useful for glob pattern matching, cross-platform comparisons, and string operations. * * @param p - Path to convert * @returns Path with forward slashes * * @example * toForwardSlash('C:\\Users\\docs\\README.md') * // Returns: 'C:/Users/docs/README.md' * * toForwardSlash('/project/docs/README.md') * // Returns: '/project/docs/README.md' (unchanged) */ export declare function toForwardSlash(p: string): string; /** * Cross-platform safe path operations. * * Wraps Node's `path.join()`, `path.resolve()`, and `path.relative()` to always * return forward-slash paths. On Windows, the native `path.*` functions return * backslashes, which causes bugs when paths are used as Map keys, compared as * strings, or matched with glob patterns. * * **Use these instead of importing from `node:path` directly.** * ESLint rules enforce this — see `no-path-join`, `no-path-resolve`, `no-path-relative`. * * @example * ```typescript * import { safePath } from '@vibe-agent-toolkit/utils'; * * // Always forward slashes, even on Windows * safePath.join('C:\\Users', 'docs', 'file.md') // → 'C:/Users/docs/file.md' * safePath.resolve('/project', './docs') // → '/project/docs' * safePath.relative('/project/docs', '/project') // → '..' * safePath.joinUnderRoot('/harness', 'skill-abc') // → '/harness/skill-abc' * safePath.joinUnderRoot('/harness', '../escape') // throws Error * ``` */ export declare const safePath: { /** Like `path.join()` but always returns forward slashes. */ readonly join: (...paths: string[]) => string; /** Like `path.resolve()` but always returns forward slashes. */ readonly resolve: (...paths: string[]) => string; /** Like `path.relative()` but always returns forward slashes. */ readonly relative: (from: string, to: string) => string; /** * Join path segments under a security root, throwing if the result would escape. * * Resolves `root + segments` and verifies the result is strictly inside `root` * (or equal to it). Throws when any segment would cause the result to escape: * * - A `..` traversal that climbs above root * - An absolute POSIX path segment (e.g. `/etc/passwd`) * - A Windows drive-letter segment (e.g. `C:\Users\evil`) * * On success returns a forward-slash-normalized absolute path (consistent with * the other `safePath` helpers). * * **Use this instead of `safePath.join(root, segment)` whenever `segment` may * contain caller-controlled input** — this is the bug class that the original * skill-test staging code was vulnerable to on Windows. * * @returns Forward-slash absolute path guaranteed to be inside `root`. * @throws {Error} If the resolved path would escape `root`. * * @example * ```typescript * // ✅ Safe — throws if caller passes '../../../etc' * const dest = safePath.joinUnderRoot(harnessRoot, stagedDirName(item.name)); * * // ❌ Unsafe — silently escapes on Windows with absolute segment * const dest = safePath.join(harnessRoot, item.name); * ``` */ readonly joinUnderRoot: (root: string, ...segments: string[]) => string; }; //# sourceMappingURL=path-core.d.ts.map