/** * Artifact boundary helpers for peaks-loop. * * - sub-slice 1: change-id validation (`validateChangeId`). * - sub-slice 2: artifact path planning (`planArtifactPath` + `isPathInsideArtifactRoot` re-export). * - sub-slice 3: workspace-unavailable response (`buildWorkspaceUnavailable`). * * The colocated single-file layout avoids creating two competing path-safety * layers — `src/shared/path-safety.ts` already owns the primitive * `isPathInsideArtifactRoot` which we re-export unchanged. * * Conventions: * - `Result` is hand-rolled in this file (no `neverthrow` dep). * - All errors are typed unions — no thrown exceptions for validation. * - The change-id format mirrors the pre-existing * `[A-Za-z0-9][A-Za-z0-9._-]*` regex used by * `openspec-validate-service.ts` so the refactor (task 3) preserves * the exact error message byte-for-byte. * - Path planning uses `posix.normalize` to keep semantics stable across * win32/posix runners; JSON output always uses `/`. */ import { isPathInsideArtifactRoot as _isPathInsideArtifactRoot } from '../../shared/path-safety.js'; export type Result = { ok: true; value: T; } | { ok: false; error: E; }; export declare function ok(value: T): Result; export declare function err(error: E): Result; export declare function isOk(result: Result): result is { ok: true; value: T; }; export type ChangeIdError = { code: 'change-id-empty'; message: string; } | { code: 'change-id-format'; message: string; } | { code: 'change-id-reserved'; message: string; }; export type BoundaryError = ChangeIdError | { code: 'workspace-unconfigured'; message: string; } | { code: 'path-outside-artifact-root'; path: string; root: string; message: string; } | { code: 'unsafe-path-input'; message: string; }; /** * Validate a change-id string. Returns a typed `Result`; the error code * distinguishes "empty" / "format" / "reserved" so callers can branch on * the cause without re-parsing the message. * * - accepts `[A-Za-z0-9][A-Za-z0-9._-]*` (existing regex semantics) * - rejects empty string with `change-id-empty` * - rejects `.` and `..` with `change-id-reserved` * - rejects everything else that does not match the regex with * `change-id-format` * * The format-error message is byte-for-byte identical to the pre-refactor * string produced at `openspec-validate-service.ts:55` so existing * `OpenSpecValidationIssue` consumers do not break. */ export declare function validateChangeId(id: string): Result<{ changeId: string; }, ChangeIdError>; /** * Re-export of `src/shared/path-safety.ts#isPathInsideArtifactRoot`. * * Single source of truth: the primitive is owned by `src/shared/path-safety.ts`; * this module owns the *domain* helpers (change-id + path planner) that consume * exercise the RE-EXPORT wiring; the primitive's own correctness is covered by * its native test file. */ export { _isPathInsideArtifactRoot as isPathInsideArtifactRoot }; /** * Input for `planArtifactPath`. The caller MUST supply `changeId`, `workspaceRoot`, * `role`, and `requestId`. Exactly one of `absolutePath` or `relativePath` may be * supplied; if neither is supplied, a default template `"//"` * is used. `template` overrides the default placeholder substitution when both * `absolutePath` and `relativePath` are absent. */ export type PlanArtifactPathInput = { changeId: string; workspaceRoot: string; role: string; requestId: string; absolutePath?: string; relativePath?: string; template?: string; }; export type PlanArtifactPathOutput = { absolutePath: string; relativePath: string; jsonSafeRelativePath: string; }; /** * Plan an artifact-relative path under the configured Peaks artifact workspace. * * - Backslashes in the candidate path are normalized to forward slashes for * JSON safety; empty segments (`foo//bar`) are collapsed by `posix.normalize`. * - If the resolved absolute path is NOT contained under `workspaceRoot` * (per `isPathInsideArtifactRoot`), returns * `BoundaryError.path-outside-artifact-root` — NO silent fallback to cwd. * - JSON output (`jsonSafeRelativePath`) ALWAYS uses `/` so it round-trips * cleanly through `JSON.stringify` on Windows. * * Path math only — this helper never touches the filesystem, so it is fully * hermetic and safe to call on paths that do not exist yet. */ export declare function planArtifactPath(input: PlanArtifactPathInput): Result; export type WorkspaceUnavailableResponse = { ok: false; mode: 'preview-only' | 'blocked'; reason: 'artifact-workspace-unavailable'; nextActions: string[]; }; /** * Build the explicit response used when no artifact workspace is configured. * The next action is static natural language so callers can surface remediation * without requiring the user to type a CLI command or hand-author data. */ export declare function buildWorkspaceUnavailable(input: { mode: WorkspaceUnavailableResponse['mode']; }): WorkspaceUnavailableResponse;