mason-context 0.20.2 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/distribution/runtime.ts","../src/automation/store.ts","../src/utils/store-lock.ts","../src/utils/storage.ts","../src/utils/paths.ts","../src/utils/git-read.ts","../src/utils/profile.ts","../src/utils/files.ts","../src/automation/notifications.ts","../src/audit/findings.ts","../src/distribution/state.ts","../src/distribution/path.ts","../src/distribution/bundle.ts","../src/distribution/trust.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport { unlinkSync } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { z } from \"zod\";\nimport { withLock } from \"../automation/store.js\";\nimport { readStoreJson, storePath, writeStoreJson } from \"../utils/storage.js\";\nimport { readInstallation, standaloneLocation } from \"./state.js\";\nimport { verifyBundle } from \"./bundle.js\";\nimport { configureSystemTrust } from \"./trust.js\";\n\nconst leaseSchema = z.object({ pid: z.number().int().positive(), host: z.string(), bundle: z.string().regex(/^[a-f0-9]{24}$/) });\n\n/** Every standalone entry point registers before executing application code. */\nexport async function registerRuntime() {\n const location = standaloneLocation();\n if (!location) return;\n configureSystemTrust();\n const { home, bundle } = location;\n const name = `runtimes/${randomUUID()}.json`;\n await withLock(home, \".install-lock\", async () => {\n await readInstallation(home);\n await fs.access(bundle);\n await writeStoreJson(home, name, { pid: process.pid, host: os.hostname(), bundle: path.basename(bundle) });\n });\n const file = await storePath(home, name);\n process.once(\"exit\", () => { try { unlinkSync(file); } catch { /* Stale leases are recovered by the next worker. */ } });\n}\n\n/** Caller holds the update lock, before staging starts; startup shares the install lock. */\nexport async function pruneUnusedVersions(home: string) {\n // Full bundle verification and recursive deletion can be slow. Keep them\n // outside the lock needed by commands and new MCP sessions.\n const versions = await storePath(home, \"versions\");\n const candidates: { id: string; directory: string; retired: boolean }[] = [];\n const initial = await readInstallation(home);\n const initialKeep = new Set([initial.current, initial.previous?.id, initial.pending?.id]);\n for (const name of await fs.readdir(versions)) {\n const retired = /^\\.retired-([a-f0-9]{24})-[a-f0-9-]+$/.exec(name);\n const id = retired?.[1] ?? name;\n if (!/^[a-f0-9]{24}$/.test(id) || initialKeep.has(id)) continue;\n try {\n const directory = await storePath(home, `versions/${name}`);\n await fs.access(await storePath(directory, \"app/dist/mason-runtime.js\"));\n const bundle = await verifyBundle(directory);\n if (bundle.manifestHash.slice(0, 24) === id && bundle.manifest.files[\"app/dist/mason-runtime.js\"])\n candidates.push({ id, directory, retired: !!retired });\n } catch { /* Legacy, edited, or unknown bundles remain untouched. */ }\n }\n if (!candidates.length) return;\n const garbage: string[] = [];\n await withLock(home, \".install-lock\", async () => {\n const record = await readInstallation(home);\n const keep = new Set([record.current, record.previous?.id, record.pending?.id]);\n const directory = await storePath(home, \"runtimes\");\n const leases = await fs.readdir(directory).catch(error => { if (error.code === \"ENOENT\") return []; throw error; });\n for (const name of leases) {\n if (!/^[a-f0-9-]+\\.json$/.test(name)) return;\n try {\n const raw = await readStoreJson(home, `runtimes/${name}`);\n if (raw === null) continue;\n const lease = leaseSchema.parse(raw);\n if (lease.host !== os.hostname()) return;\n try { process.kill(lease.pid, 0); keep.add(lease.bundle); }\n catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ESRCH\") return;\n await fs.rm(await storePath(home, `runtimes/${name}`), { force: true });\n }\n } catch { return; } // Uncertain process ownership means no deletion.\n }\n for (const candidate of candidates) {\n if (keep.has(candidate.id)) continue;\n try {\n const retired = candidate.retired ? candidate.directory\n : await storePath(home, `versions/.retired-${candidate.id}-${randomUUID()}`);\n if (!candidate.retired) await fs.rename(candidate.directory, retired);\n garbage.push(retired);\n } catch { /* Busy files remain available for the next check. */ }\n }\n });\n for (const directory of garbage) await fs.rm(directory, { recursive: true }).catch(() => {});\n}\n","import { z } from \"zod\";\nimport { withStoreLock } from \"../utils/store-lock.js\";\nimport { notificationStateSchema } from \"./notifications.js\";\n\nexport const hostSchema = z.enum([\"claude\", \"codex\"]);\nexport type Host = z.infer<typeof hostSchema>;\nexport const events = [\"session_start\", \"turn_start\", \"before_tool\", \"after_tool\", \"task_end\"] as const;\nexport type Event = typeof events[number];\nexport const stateSchema = z.object({\n version: z.literal(1), root: z.string(), gitDir: z.string(), branch: z.string(),\n baselines: z.array(z.object({ path: z.string(), at: z.string(), event: z.string(), fingerprint: z.string() })).max(128),\n sessions: z.record(z.object({\n host: hostSchema,\n lastUsed: z.string(),\n notifiedDiagnostics: z.array(z.string()).default([]),\n pending: z.record(z.string()), coverageGaps: z.array(z.string()),\n events: z.record(z.object({ at: z.string(), count: z.number().int().positive() })),\n })),\n updatedAt: z.string(), fingerprint: z.string().nullable(),\n latest: z.string().nullable(),\n notifications: notificationStateSchema.optional(),\n agentNotifications: notificationStateSchema.optional(),\n analysis: z.object({ id: z.string(), fingerprint: z.string(), completedAt: z.number(), path: z.string() }).optional(),\n});\nexport type State = z.infer<typeof stateSchema>;\nexport function parseState(raw: unknown): State {\n try { return stateSchema.parse(raw); }\n catch (error) { throw new Error(\"Invalid automation state; original evidence was retained.\", { cause: error }); }\n}\n\n/** Preserve the automation lock location and diagnostics for existing callers. */\nexport function withLock<T>(root: string, directory: string, run: () => Promise<T>, waitMs = 5000): Promise<T> {\n return withStoreLock(root, directory, run, waitMs, \"Automation\");\n}\n","import fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport { storePath } from \"./storage.js\";\n\nasync function lockDiagnostic(file: string): Promise<string> {\n let reason: string;\n try {\n const owner = JSON.parse(await fs.readFile(file, \"utf8\"));\n if (!owner || typeof owner.host !== \"string\" || !Number.isInteger(owner.pid) || owner.pid <= 0) {\n reason = \"Lock owner data is incomplete or malformed.\";\n } else if (owner.host !== os.hostname()) {\n reason = \"The lock belongs to another host; its process cannot be checked locally.\";\n } else {\n try { process.kill(owner.pid, 0); reason = `Local owner PID ${owner.pid} is still running.`; }\n catch (error) {\n reason = (error as NodeJS.ErrnoException).code === \"ESRCH\"\n ? `Local owner PID ${owner.pid} has exited, but lock recovery has not completed.`\n : `Local owner PID ${owner.pid} could not be checked.`;\n }\n }\n } catch (error) {\n reason = error instanceof SyntaxError ? \"Lock owner data is incomplete or malformed.\" : \"Lock owner data could not be read; the lock may have changed.\";\n }\n if (await fs.lstat(file + \".reclaim\").then(() => true, () => false)) {\n reason += \" A recovery guard also needs inspection: \" + file + \".reclaim.\";\n }\n return reason + \" Retry if a writer is active. For manual recovery, stop all Mason writers for this checkout, confirm they have exited (including other hosts), then remove only confirmed abandoned lock and lock.reclaim files. See docs/reference.md#snapshot-repairs-and-verification.\";\n}\n\n/** Cross-process lock: a killed writer's lock is reclaimed only after its local PID is gone. */\nexport async function withStoreLock<T>(root: string, directory: string, run: () => Promise<T>, waitMs = 5000, label = \"Mason store\"): Promise<T> {\n const file = await storePath(root, directory + \"/lock\", true);\n const deadline = Date.now() + waitMs;\n let handle;\n while (!handle) {\n try {\n handle = await fs.open(file, \"wx\", 0o600);\n } catch (error) {\n // Windows can deny opening a lock while another process finishes deleting it.\n // Retry acquisition only, retaining the original error if access stays denied.\n if (process.platform === \"win32\" && (error as NodeJS.ErrnoException).code === \"EPERM\") {\n if (Date.now() >= deadline) throw error;\n await new Promise(resolve => setTimeout(resolve, 40));\n continue;\n }\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n // Do not steal malformed, remote, or live locks on a time-based guess.\n try {\n const owner = JSON.parse(await fs.readFile(file, \"utf8\"));\n if (owner.host === os.hostname() && Number.isInteger(owner.pid) && owner.pid > 0) {\n try { process.kill(owner.pid, 0); }\n catch (probe) {\n if ((probe as NodeJS.ErrnoException).code === \"ESRCH\") {\n // A separate guard arbitrates reclamation; recheck the owner before unlinking.\n const reclaim = file + \".reclaim\";\n let guard;\n try {\n guard = await fs.open(reclaim, \"wx\", 0o600);\n const current = JSON.parse(await fs.readFile(file, \"utf8\"));\n if (current.pid === owner.pid && current.host === owner.host) await fs.unlink(file);\n } finally { if (guard) { await guard.close(); await fs.rm(reclaim, { force: true }); } }\n }\n }\n }\n } catch { /* Another writer may be creating/releasing/reclaiming it. */ }\n if (Date.now() >= deadline) throw new Error(label + \" is busy or its lock needs inspection: \" + file + \". \" + await lockDiagnostic(file));\n await new Promise(resolve => setTimeout(resolve, 40));\n }\n if (handle) {\n try { await handle.writeFile(JSON.stringify({ pid: process.pid, host: os.hostname() })); }\n catch (error) {\n await handle.close().catch(() => {});\n await fs.rm(file, { force: true }).catch(() => {});\n throw error;\n }\n }\n }\n try { return await run(); }\n finally { await handle.close(); await fs.unlink(file); }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { normalizeRepoPath } from \"./paths.js\";\nimport { readBoundedFile } from \"./files.js\";\n\nexport interface StoreDiagnostic { path: string; message: string }\n\n/** Metadata paths may not contain symlinks, including their parent directories. */\nexport async function storePath(root: string, relative: string, createParents = false): Promise<string> {\n const normalized = normalizeRepoPath(relative);\n if (!normalized) throw new Error(`Invalid store path: ${relative}`);\n let current = await fs.realpath(root);\n const parts = normalized.split(\"/\");\n for (let i = 0; i < parts.length; i++) {\n current = path.join(current, parts[i]);\n let stat;\n try { stat = await fs.lstat(current); } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n if (createParents && i < parts.length - 1) {\n try { await fs.mkdir(current); } catch (mkdirError) {\n if ((mkdirError as NodeJS.ErrnoException).code !== \"EEXIST\") throw mkdirError;\n }\n stat = await fs.lstat(current);\n }\n }\n if (stat?.isSymbolicLink()) throw new Error(`Symlink in store path: ${relative}`);\n }\n return current;\n}\n\nexport async function readStoreJson(root: string, relative: string): Promise<unknown | null> {\n try {\n const file = await storePath(root, relative);\n const raw = await readBoundedFile(file, 10 * 1024 * 1024);\n if (raw === null) throw new Error(\"file is not regular or exceeds 10 MiB\");\n const parsed: unknown = JSON.parse(raw);\n if (parsed === null) throw new Error(\"expected a JSON object, received null\");\n return parsed;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw new Error(`Invalid Mason store ${relative}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });\n }\n}\n\nexport async function writeStoreJson(root: string, relative: string, value: unknown): Promise<void> {\n const payload = JSON.stringify(value, null, 2) + \"\\n\";\n if (Buffer.byteLength(payload) > 10 * 1024 * 1024) {\n throw new Error(`Mason store ${relative} exceeds 10 MiB`);\n }\n const file = await storePath(root, relative, true);\n const temporary = path.join(path.dirname(file), `.${path.basename(file)}.${randomUUID()}.tmp`);\n try {\n const handle = await fs.open(temporary, \"wx\", 0o600);\n try { await handle.writeFile(payload, \"utf8\"); await handle.sync(); }\n finally { await handle.close(); }\n await fs.rename(temporary, file);\n } finally { await fs.rm(temporary, { force: true }); }\n}\n","import path from \"node:path\";\n\n/** One canonical representation for stored paths and decision anchors. */\nexport function normalizeRepoPath(value: string): string | null {\n const slash = value.replace(/\\\\/g, \"/\");\n if (!slash || slash.includes(\"\\0\") || path.posix.isAbsolute(slash) || /^[A-Za-z]:/.test(slash)) return null;\n if (slash.split(\"/\").includes(\"..\")) return null;\n const normalized = path.posix.normalize(slash).replace(/\\/$/, \"\");\n return normalized === \".\" ? null : normalized;\n}\n\nexport function sanitizeRepoPaths(files: string[]): string[] {\n return [...new Set(files.map(normalizeRepoPath).filter((p): p is string => p !== null))];\n}\n\nexport function isWithinRoot(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative !== \"..\" && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);\n}\n\nexport function anchorMatches(anchor: string, file: string): boolean {\n const a = normalizeRepoPath(anchor);\n const f = normalizeRepoPath(file);\n return a !== null && f !== null && (a === f || f.startsWith(`${a}/`));\n}\n\nexport function matchingPaths(anchors: string[], files: Iterable<string>): string[] {\n return [...new Set(files)].filter(file => anchors.some(anchor => anchorMatches(anchor, file)));\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { profilePhase } from \"./profile.js\";\n\nconst execute = promisify(execFile);\nconst commands = new Set([\"rev-parse\", \"symbolic-ref\", \"for-each-ref\", \"ls-files\", \"check-ignore\", \"status\", \"log\", \"diff\", \"ls-tree\", \"show\", \"merge-base\"]);\nexport interface GitReadOptions { cwd: string; maxBuffer?: number; timeout?: number; input?: string }\n\n/** Fixed command labels only: profiling never includes arguments or repository paths. */\nexport function execGit(args: string[], options: GitReadOptions) {\n const command = args.find(arg => !arg.startsWith(\"-\")) ?? \"other\";\n return profilePhase(\"git.\" + (commands.has(command) ? command : \"other\"), async () => {\n const { input, ...processOptions } = options;\n const operation = execute(\"git\", args, processOptions);\n if (input !== undefined) {\n operation.child.stdin!.on(\"error\", () => { /* The child exit reports a failed query. */ });\n operation.child.stdin!.end(input);\n }\n return operation;\n });\n}\n","import { AsyncLocalStorage } from \"node:async_hooks\";\n\ntype Timing = { calls: number; totalMs: number };\nconst profiles = new AsyncLocalStorage<Record<string, Timing>>();\n\n/** Opt-in, invocation-local timings. Labels are fixed in source; no repository data. */\nexport async function profilePhase<T>(label: string, run: () => Promise<T>): Promise<T> {\n const phases = profiles.getStore();\n if (!phases) return run();\n const start = performance.now();\n try { return await run(); }\n finally {\n const timing = phases[label] ??= { calls: 0, totalMs: 0 };\n timing.calls++;\n timing.totalMs += performance.now() - start;\n }\n}\n\nexport async function withProfile<T>(run: () => Promise<T>, emit: (json: string) => void): Promise<T> {\n const phases: Record<string, Timing> = {};\n const start = performance.now();\n return profiles.run(phases, async () => {\n try { return await run(); }\n finally {\n emit(JSON.stringify({ kind: \"mason-profile\", version: 1, totalMs: performance.now() - start, phases }));\n }\n });\n}\n","import { execGit } from \"./git-read.js\";\nimport fs from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport path from \"node:path\";\nimport fg from \"fast-glob\";\nimport { isWithinRoot, normalizeRepoPath } from \"./paths.js\";\n\nexport const SOURCE_EXTENSIONS = [\"ts\", \"tsx\", \"js\", \"jsx\", \"mts\", \"cts\", \"mjs\", \"cjs\", \"vue\", \"svelte\", \"kt\", \"kts\", \"java\", \"py\", \"go\", \"rs\", \"swift\", \"rb\", \"cs\", \"cpp\", \"c\", \"h\", \"hpp\", \"dart\", \"php\"];\nexport const SOURCE_GLOB = `**/*.{${SOURCE_EXTENSIONS.join(\",\")}}`;\nexport const SOURCE_IGNORE = [\n \"**/node_modules/**\", \"**/dist/**\", \"**/build/**\", \"**/.gradle/**\",\n \"**/target/**\", \"**/.git/**\", \"**/.mason/**\", \"**/vendor/**\", \"**/__pycache__/**\",\n \"**/venv/**\", \"**/.venv/**\", \"**/*.min.*\", \"**/*.map\", \"**/*.lock\",\n \"**/generated/**\", \"**/*.generated.*\", \"**/R.java\", \"**/BuildConfig.java\",\n \"**/package-lock.json\", \"**/yarn.lock\", \"**/pnpm-lock.yaml\",\n];\nexport const MAX_SOURCE_BYTES = 1024 * 1024;\nexport interface ProjectConfig {\n patterns?: string[]; alwaysInclude?: string[]; ignore?: string[];\n audit?: { include?: string[]; exclude?: Partial<Record<import(\"../audit/types.js\").CheckName, string[]>> };\n}\nexport interface SourceFile { path: string; content: string; totalLines: number }\n\nexport function isSensitiveFile(file: string): boolean {\n return file.split(/[\\\\/]/).some(part =>\n /^(?:\\.env(?:\\..*)?|id_rsa.*|id_ed25519.*)$|\\.(?:pem|key|p12|pfx|jks|keystore)$|credentials\\.|secret|^local\\.properties$/i.test(part)\n );\n}\n\n/** Bound reads even if a file grows after stat. Only read regular files. */\nexport async function readBoundedFile(file: string, maxBytes: number): Promise<string | null> {\n // Do not block on a FIFO or follow a symlink substituted after resolution.\n const handle = await fs.open(file, constants.O_RDONLY | constants.O_NONBLOCK | constants.O_NOFOLLOW);\n try {\n const stat = await handle.stat();\n if (!stat.isFile() || stat.size > maxBytes) return null;\n const buffer = Buffer.alloc(Math.min(maxBytes + 1, stat.size + 1));\n let bytes = 0;\n while (bytes < buffer.length) {\n const result = await handle.read(buffer, bytes, buffer.length - bytes, null);\n if (result.bytesRead === 0) break;\n bytes += result.bytesRead;\n }\n return bytes === buffer.length ? null : buffer.subarray(0, bytes).toString(\"utf8\");\n } finally { await handle.close(); }\n}\n\nexport async function loadProjectConfig(root: string): Promise<ProjectConfig> {\n try {\n const canonicalRoot = await fs.realpath(root);\n const configPath = await fs.realpath(path.join(root, \".mason/config.json\"));\n if (!isWithinRoot(canonicalRoot, configPath)) throw new Error(\"Project configuration resolves outside the repository\");\n const raw = await readBoundedFile(configPath, 64 * 1024);\n if (raw === null) throw new Error(\"Project configuration is not a regular file or exceeds 64 KiB\");\n const value = JSON.parse(raw);\n if (!value || typeof value !== \"object\" || Array.isArray(value)) throw new Error(\"Expected a configuration object\");\n const config: ProjectConfig = {};\n for (const key of [\"patterns\", \"alwaysInclude\", \"ignore\"] as const) {\n if (value[key] === undefined) continue;\n if (!Array.isArray(value[key]) || !value[key].every((s: unknown) => typeof s === \"string\")) {\n throw new Error(`Configuration ${key} must be an array of strings`);\n }\n config[key] = value[key];\n }\n if (value.audit !== undefined) {\n const audit = value.audit;\n if (!audit || typeof audit !== \"object\" || Array.isArray(audit)) throw new Error(\"Configuration audit must be an object\");\n const patterns = (raw: unknown): string[] => {\n if (!Array.isArray(raw) || raw.length > 100 || !raw.every(s => typeof s === \"string\" &&\n normalizeRepoPath(s) === s && !s.startsWith(\"!\") && !/[\\x00-\\x1f]/.test(s))) {\n throw new Error(\"Audit patterns must be at most 100 repository-relative globs without traversal or negation\");\n }\n return raw;\n };\n config.audit = {};\n if (audit.include !== undefined) config.audit.include = patterns(audit.include);\n if (audit.exclude !== undefined) {\n if (!audit.exclude || typeof audit.exclude !== \"object\" || Array.isArray(audit.exclude)) throw new Error(\"audit.exclude must map check names to patterns\");\n const { ALL_CHECKS } = await import(\"../audit/types.js\");\n config.audit.exclude = {};\n for (const [check, globs] of Object.entries(audit.exclude)) {\n if (!ALL_CHECKS.includes(check as typeof ALL_CHECKS[number])) throw new Error(\"Unknown audit check: \" + check);\n config.audit.exclude[check as typeof ALL_CHECKS[number]] = patterns(globs);\n }\n }\n }\n return config;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return {};\n throw new Error(`Cannot apply project file policy: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n\n/** Scoped to one operation, so a later tool call sees newly edited files/ignores. */\nexport async function createFileAccess(rootDir: string) {\n const root = path.resolve(rootDir);\n const canonicalRoot = await fs.realpath(root).catch(() => root);\n const config = await loadProjectConfig(root);\n const ignore = [...SOURCE_IGNORE, ...(config.ignore ?? [])];\n let gitFiles: Set<string> | null = null;\n try {\n const { stdout } = await execGit([\"ls-files\", \"-z\", \"--cached\", \"--others\", \"--exclude-standard\"], { cwd: root, maxBuffer: 50 * 1024 * 1024 });\n gitFiles = new Set(stdout.split(\"\\0\").filter(Boolean));\n } catch {\n // File-system projects are supported. Fail closed if this IS a Git repo.\n let inGit = false;\n try { await execGit([\"rev-parse\", \"--git-dir\"], { cwd: root }); inGit = true; } catch { /* no Git */ }\n if (inGit) throw new Error(\"Cannot enumerate Git files safely\");\n }\n\n async function resolve(file: string): Promise<string | null> {\n const relative = normalizeRepoPath(file);\n if (!relative || isSensitiveFile(relative) || (gitFiles && !gitFiles.has(relative))) return null;\n const candidate = path.join(root, relative);\n try {\n const real = await fs.realpath(candidate);\n if (!isWithinRoot(canonicalRoot, real) || isSensitiveFile(path.relative(canonicalRoot, real))) return null;\n const stat = await fs.stat(real);\n if (!stat.isFile() || stat.size > MAX_SOURCE_BYTES) return null;\n // A symlink must not bypass the target's ignore policy either.\n if (gitFiles && !gitFiles.has(path.relative(canonicalRoot, real).split(path.sep).join(\"/\"))) return null;\n return real;\n } catch { return null; }\n }\n\n async function list(patterns: string | string[] = SOURCE_GLOB, options: { deep?: number; dot?: boolean } = {}): Promise<string[]> {\n const found = await fg(patterns, { cwd: root, ignore, followSymbolicLinks: false, ...options });\n const safe = await Promise.all(found.map(async f => (await resolve(f)) ? f : null));\n return safe.filter((f): f is string => f !== null).sort();\n }\n\n async function read(file: string): Promise<SourceFile | null> {\n const relative = normalizeRepoPath(file);\n if (!relative) return null;\n const real = await resolve(relative);\n if (!real) return null;\n // Apply the same glob exclusions to explicit reads and symlink targets.\n for (const rel of new Set([relative, path.relative(canonicalRoot, real).split(path.sep).join(\"/\")])) {\n if (!(await fg(fg.escapePath(rel), { cwd: root, ignore, dot: true })).length) return null;\n }\n try {\n const content = await readBoundedFile(real, MAX_SOURCE_BYTES);\n return content === null ? null : { path: relative, content, totalLines: content.split(\"\\n\").length };\n } catch { return null; }\n }\n return { root, config, list, read };\n}\n","import { z } from \"zod\";\nimport type { RepairFinding } from \"../audit/repair.js\";\nimport { digest } from \"../audit/findings.js\";\n\n// Compare conditions, not report wording, line numbers, commit IDs, or timestamps.\nconst conditionSchema = z.object({\n level: z.number(), facts: z.array(z.string()), magnitude: z.number().optional(),\n});\nconst MAX_COMPLETION_FINDINGS = 4;\n\ntype Condition = z.infer<typeof conditionSchema>;\nexport const notificationStateSchema = z.object({\n version: z.literal(1), baselineHead: z.string(),\n baseline: z.record(conditionSchema),\n delivered: z.record(conditionSchema),\n cleared: z.array(z.string()),\n});\nexport type NotificationState = z.infer<typeof notificationStateSchema>;\n\nfunction condition(finding: RepairFinding): { id: string; condition: Condition } {\n const source = finding.current ?? finding.original;\n const e = source.evidence;\n // Decision approval/revision changes alone do not create a new drift condition.\n const id = e.kind === \"decision-anchor\" ? digest([source.type, source.anchor.doc, e.decisionId]) : finding.id;\n const result: Condition = { level: \"confidence\" in source ? 2 : 1, facts: [] };\n switch (e.kind) {\n case \"count-mismatch\": result.magnitude = Math.abs(e.actual - e.claimed); break;\n case \"unmentioned-dir\": result.magnitude = e.sourceFileCount; break;\n case \"decision-anchor\": result.facts = [...e.changedFiles].sort(); break;\n case \"doc-behind-manifests\":\n result.facts = (e.matches ?? []).map(m => JSON.stringify([m.manifest, m.dependency, m.after])).sort();\n break;\n // For missing paths/scripts, the finding ID already identifies the condition.\n }\n return { id, condition: result };\n}\n\nexport function notificationConditions(findings: RepairFinding[]): Record<string, Condition> {\n const active = findings.filter(f => f.status !== \"resolved\");\n const currentIds = new Set(active.filter(f => f.current).map(f => condition(f).id));\n const result: Record<string, Condition> = {};\n for (const f of active) {\n const value = condition(f);\n // Historical revisions must not override current evidence for the same decision.\n if (!f.current && currentIds.has(value.id)) continue;\n const previous = result[value.id];\n result[value.id] = previous ? {\n level: Math.max(previous.level, value.condition.level),\n facts: [...new Set([...previous.facts, ...value.condition.facts])].sort(),\n ...(previous.magnitude !== undefined || value.condition.magnitude !== undefined\n ? { magnitude: Math.max(previous.magnitude ?? 0, value.condition.magnitude ?? 0) } : {}),\n } : value.condition;\n }\n return result;\n}\n\nexport function createNotificationState(findings: RepairFinding[], head: string): NotificationState {\n return { version: 1, baselineHead: head, baseline: notificationConditions(findings), delivered: {}, cleared: [] };\n}\n\nfunction worsened(current: Condition, previous?: Condition): boolean {\n return !previous || current.level > previous.level ||\n (current.magnitude ?? 0) > (previous.magnitude ?? 0) ||\n current.facts.some(fact => !previous.facts.includes(fact));\n}\n\n/** Each delivery channel owns its state; explicit audits never consume notifications. */\nexport function completionFindings(state: NotificationState, findings: RepairFinding[]): RepairFinding[] {\n const current = notificationConditions(findings);\n const cleared = new Set(state.cleared);\n const resolvedIds = new Set(findings.filter(f => f.status === \"resolved\").map(f => condition(f).id));\n for (const id of Object.keys(state.baseline)) if (resolvedIds.has(id) && !current[id]) cleared.add(id);\n state.cleared = [...cleared];\n\n const selected: RepairFinding[] = [];\n const selectedIds = new Set<string>();\n const delivered: Record<string, Condition> = { ...state.delivered };\n for (const finding of findings) {\n const value = condition(finding);\n const previous = state.delivered[value.id];\n value.condition = current[value.id] ?? value.condition;\n // Missing or temporarily unverified evidence must not erase delivery history.\n if (finding.status === \"unverified\" || !finding.current && finding.status !== \"resolved\") {\n continue;\n }\n if (finding.status === \"resolved\") {\n if (!current[value.id]) delete delivered[value.id];\n continue;\n }\n const eligible = cleared.has(value.id) || worsened(value.condition, state.baseline[value.id]);\n if (!eligible) continue;\n if (!selectedIds.has(value.id) && worsened(value.condition, previous)) {\n selectedIds.add(value.id);\n const source = finding.current!;\n selected.push(source.evidence.kind === \"decision-anchor\"\n ? { ...finding, current: { ...source, evidence: { ...source.evidence, changedFiles: value.condition.facts } } }\n : finding);\n // Only displayed findings count as delivered. Overflow remains eligible\n // for the next completion, including after a session restart.\n if (selected.length <= MAX_COMPLETION_FINDINGS) delivered[value.id] = value.condition;\n }\n }\n state.delivered = delivered;\n return selected;\n}\n\nexport function completionSummary(findings: RepairFinding[], reportPath: string): string | null {\n if (!findings.length) return null;\n const safe = (value: string) => value.replace(/[\\u0000-\\u001f\\u007f-\\u009f]/g, \" \").slice(0, 250);\n return [\n `Mason: ${findings.length} new or worsened finding(s) since the retained baseline.`,\n ...findings.slice(0, MAX_COMPLETION_FINDINGS).map(f => {\n const source = f.current ?? f.original;\n if (source.evidence.kind === \"decision-anchor\") {\n const evidence = source.evidence;\n return [\n `[advisory] Decision \"${safe(evidence.title)}\" may no longer hold: ${evidence.changedFiles.length} anchored file(s) changed.`,\n `Inspect the decision and diff with review_decision(action: \"prepare\", id: ${JSON.stringify(evidence.decisionId)}); acceptance is not required.`,\n `Changed files: ${safe(evidence.changedFiles.join(\", \"))}`,\n ].join(\"\\n\");\n }\n return `[${f.status === \"unresolved\" ? \"issue\" : \"advisory\"}] ${safe(source.anchor.doc)}: ${safe(source.message)}`;\n }),\n ...(findings.length > MAX_COMPLETION_FINDINGS ? [`${findings.length - MAX_COMPLETION_FINDINGS} more in the report; pending later feedback.`] : []),\n `Evidence: ${reportPath}. Explicit audit: mason_automation(action: \"check\").`,\n \"Inspect findings within the authorized work. Concurrent changes may also contribute; this summary does not establish who introduced them.\",\n ].join(\"\\n\");\n}\n","import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\nimport type { AuditIssue, AuditAdvisory } from \"./types.js\";\nexport type Finding = AuditIssue | AuditAdvisory;\nexport const digest = (value: unknown) => createHash(\"sha256\").update(JSON.stringify(value)).digest(\"hex\");\nexport const checkSchema = z.enum([\"deleted-reference\", \"new-module\", \"stale-count\", \"dead-command\", \"deps-changed\", \"decision-anchor-drift\"]);\nexport const commitSchema = z.object({ hash: z.string().regex(/^[a-f0-9]{40,64}$/), date: z.string(), subject: z.string() });\nconst anchorSchema = z.object({ doc: z.string(), line: z.number().int().positive().nullable(), excerpt: z.string().nullable() });\nconst count = z.number().int().nonnegative();\nconst scopeSchema = z.object({ basis: z.enum([\"repository\", \"document\", \"document-link\", \"explicit\"]),\n directory: z.string(), candidates: z.array(z.string()) });\nconst evidenceSchema = z.discriminatedUnion(\"kind\", [\n z.object({ kind: z.literal(\"missing-path\"), claimed: z.string(), renamedTo: z.string().nullable(),\n deletedInCommit: commitSchema.nullable(), everTracked: z.boolean(), parentDirExists: z.boolean(), resolvedPath: z.string().optional(), scope: scopeSchema.optional() }),\n z.object({ kind: z.literal(\"unmentioned-dir\"), dir: z.string(), sourceFileCount: count,\n firstCommit: commitSchema.nullable(), checkedDocs: z.array(z.string()) }),\n z.object({ kind: z.literal(\"count-mismatch\"), claimed: count, actual: count, unit: z.string(),\n countedFrom: z.string(), members: z.array(z.string()) }),\n z.object({ kind: z.literal(\"missing-script\"), scriptName: z.string(), invocation: z.string(),\n manifestsChecked: z.array(z.string()), availableScripts: z.array(z.string()), scope: scopeSchema.optional() }),\n z.object({ kind: z.literal(\"doc-behind-manifests\"), docLastCommit: commitSchema,\n manifestCommits: z.array(commitSchema.extend({ files: z.array(z.string()) })), totalCommits: count,\n matches: z.array(z.object({ dependency: z.string(), manifest: z.string(), before: z.string().nullable(), after: z.string().nullable(), line: z.number().int().positive(), excerpt: z.string() })).optional(),\n matchingIncomplete: z.boolean().optional() }),\n z.object({ kind: z.literal(\"decision-anchor\"), decisionId: z.string(), title: z.string(),\n changedFiles: z.array(z.string()), refreshedHash: z.string(),\n provenance: z.object({}).passthrough().optional() }),\n]);\nconst findingSchema = z.object({ message: z.string(), anchor: anchorSchema, evidence: evidenceSchema });\nexport const issueSchema = findingSchema.extend({\n type: z.enum([\"deleted-reference\", \"new-module\", \"stale-count\", \"dead-command\"]),\n confidence: z.enum([\"certain\", \"likely\"]),\n});\nexport const advisorySchema = findingSchema.extend({ type: checkSchema, resolution: z.enum([\"recheck\", \"informational\"]).optional() });\nexport const checkResultSchema = z.object({\n issues: z.array(issueSchema), advisories: z.array(advisorySchema),\n suppressedAdvisories: z.array(advisorySchema).optional(),\n skipped: z.array(z.object({ check: z.string(), reason: z.string(), doc: z.string().optional() })),\n});\n/** Lines and wording can change without changing the underlying claim. */\nexport function findingId(finding: Finding): string {\n const e = finding.evidence;\n let key: unknown;\n switch (e.kind) {\n case \"missing-path\": key = e.resolvedPath && e.resolvedPath !== e.claimed ? [e.claimed, e.resolvedPath] : e.claimed; break;\n case \"unmentioned-dir\": key = e.dir; break;\n case \"count-mismatch\": key = [e.unit.replace(/s$/, \"\"), e.countedFrom]; break;\n case \"missing-script\": key = e.scope && e.scope.directory !== \".\" ? [e.scriptName, e.scope.directory] : e.scriptName; break;\n case \"doc-behind-manifests\": key = null; break;\n case \"decision-anchor\": key = [e.decisionId, e.provenance?.revision, e.provenance?.approval]; break;\n }\n return digest([finding.type, finding.anchor.doc, key]);\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport fs from \"node:fs/promises\";\nimport { z } from \"zod\";\nimport { readStoreJson } from \"../utils/storage.js\";\nimport { pathChangeSchema } from \"./path.js\";\n\nexport const releaseVersion = z.string().regex(/^\\d+\\.\\d+\\.\\d+(?:-[A-Za-z0-9.-]+)?$/);\nconst bundleId = z.string().regex(/^[a-f0-9]{24}$/);\nexport const updatePolicySchema = z.object({\n enabled: z.boolean(), revision: z.string(), pinnedVersion: releaseVersion.optional(), mirror: z.string().optional(),\n});\nexport const recordSchema = z.object({\n format: z.literal(1), home: z.string(), bin: z.string(), current: bundleId, version: releaseVersion,\n launchers: z.record(z.string()), previousLaunchers: z.record(z.string()).optional(),\n pathChanges: z.array(pathChangeSchema).optional(), updates: updatePolicySchema.optional(),\n previous: z.object({ id: bundleId, version: releaseVersion }).optional(),\n pending: z.object({ id: bundleId, version: releaseVersion, policyRevision: z.string() }).optional(),\n});\nexport type InstallRecord = z.infer<typeof recordSchema>;\nexport const defaultHome = () => process.env.MASON_HOME ?? (process.platform === \"win32\"\n ? path.join(process.env.LOCALAPPDATA ?? os.homedir(), \"Mason\") : path.join(os.homedir(), \".local/share/mason\"));\n\nexport async function readInstallation(home: string): Promise<InstallRecord> {\n const raw = await readStoreJson(home, \"install.json\");\n if (!raw) throw new Error(\"Run this command from a standalone installation. npm users can upgrade with npm.\");\n const record = recordSchema.parse(raw);\n if (record.home !== await fs.realpath(home)) throw new Error(\"Invalid standalone installation receipt.\");\n return record;\n}\n\nexport function standaloneLocation() {\n // Distribution modules are bundled into app/dist/mason.js (or mason-mcp.js).\n const bundle = path.resolve(path.dirname(fileURLToPath(import.meta.url)), \"../..\");\n const home = path.dirname(path.dirname(bundle));\n if (path.dirname(bundle) !== path.join(home, \"versions\") || !/^[a-f0-9]{24}$/.test(path.basename(bundle))) {\n return null;\n }\n return { home, bundle };\n}\n\nexport async function currentInstallation() {\n const location = standaloneLocation();\n if (!location) throw new Error(\"Run this command from a standalone installation. npm users can upgrade with npm.\");\n const { home, bundle } = location;\n const record = await readInstallation(home);\n return { home, bundle, record };\n}\n\nexport function automaticUpdatesAllowed(record: InstallRecord, env: NodeJS.ProcessEnv = process.env): boolean {\n return record.updates?.enabled === true && !record.updates.pinnedVersion && !record.updates.mirror\n && !env.CI && env.MASON_NO_AUTO_UPDATE !== \"1\" && !env.MASON_RELEASE_BASE && !env.MASON_VERSION;\n}\n\n/** Automatic updates accept stable releases only and never downgrade. */\nexport function newerStableVersion(candidate: string, installed: string): boolean {\n if (!/^\\d+\\.\\d+\\.\\d+$/.test(candidate)) return false;\n const a = candidate.split(\".\").map(BigInt), b = installed.split(\"-\")[0].split(\".\").map(BigInt);\n for (let i = 0; i < 3; i++) { if (a[i] !== b[i]) return a[i] > b[i]; }\n return installed.includes(\"-\");\n}\n","import fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { z } from \"zod\";\n\nconst execute = promisify(execFile);\nexport const pathChangeSchema = z.discriminatedUnion(\"kind\", [\n z.object({ kind: z.literal(\"profile\"), file: z.string(), block: z.string(), created: z.boolean() }),\n z.object({ kind: z.literal(\"windows\"), entry: z.string(), created: z.boolean() }),\n]);\nexport type PathChange = z.infer<typeof pathChangeSchema>;\nexport type PathResult = { status: \"configured\" | \"manual\"; message: string; changes: PathChange[] };\nexport type PathOptions = { platform?: NodeJS.Platform; userHome?: string; env?: NodeJS.ProcessEnv };\nconst quote = (s: string) => \"'\" + s.replace(/'/g, \"'\\\\''\") + \"'\";\n\n// Store the ownership intent before changing settings, so interrupted installs\n// can resume and uninstall can distinguish our addition from existing settings.\nexport async function configurePath(bin: string, previous: PathChange[], save: (changes: PathChange[]) => Promise<void>, options: PathOptions = {}): Promise<PathResult> {\n const env = options.env ?? process.env, platform = options.platform ?? process.platform;\n const changes = [...previous];\n // Profile/registry changes affect future shells. The inherited PATH tells us\n // whether this terminal already has the installation directory available.\n const currentPath = platform === \"win32\" ? Object.entries(env).find(([key]) => key.toLowerCase() === \"path\")?.[1] : env.PATH;\n const alreadyOnPath = platform === \"win32\" ? hasWindowsEntry(currentPath ?? null, bin, env)\n : (currentPath ?? \"\").split(\":\").some(entry => path.posix.isAbsolute(entry) && path.posix.resolve(entry) === path.posix.resolve(bin));\n const setupHint = \"To connect a project, run one of these inside its directory:\\n Codex: mason setup --host codex\\n Claude Code: mason setup --host claude\";\n const message = (alreadyOnPath ? \"\" : \"Open a new terminal to use mason.\\n\") + setupHint;\n const manual = (reason: string): PathResult => ({ status: \"manual\", changes, message: `${reason}\\n${alreadyOnPath ? \"\" : `Add ${bin} to your PATH.\\n`}${setupHint}` });\n if (env.MASON_NO_MODIFY_PATH === \"1\") return manual(\"Automatic PATH setup is disabled (MASON_NO_MODIFY_PATH=1).\");\n if (/[\\r\\n\\0]/.test(bin) || bin.includes(platform === \"win32\" ? \";\" : \":\")) return manual(\"This installation path cannot be represented safely in PATH.\");\n try {\n if (platform === \"win32\") {\n const current = await windowsPath(\"read\");\n if (!hasWindowsEntry(current.value, bin)) {\n if (!changes.some(c => c.kind === \"windows\" && c.entry === bin)) {\n changes.push({ kind: \"windows\", entry: bin, created: current.value === null }); await save(changes);\n }\n await windowsPath(\"add\", bin, current);\n }\n return { status: \"configured\", changes, message };\n }\n const shell = path.basename(env.SHELL ?? os.userInfo().shell ?? \"\");\n const userHome = options.userHome ?? os.homedir();\n let profiles: string[];\n if (![\"bash\", \"zsh\", \"sh\"].includes(shell)) return manual(`Automatic PATH setup is unavailable for ${shell || \"an unknown shell\"}.`);\n if (env.MASON_PROFILE) profiles = [path.resolve(env.MASON_PROFILE)];\n else if (shell === \"zsh\") profiles = [path.join(env.ZDOTDIR || userHome, \".zshrc\")];\n else if (shell === \"sh\") profiles = [path.join(userHome, \".profile\")];\n else {\n const candidates = [\".bash_profile\", \".bash_login\", \".profile\"].map(name => path.join(userHome, name));\n let login = candidates[2];\n for (const file of candidates) if (await fs.lstat(file).then(() => true, error => { if (error.code === \"ENOENT\") return false; throw error; })) { login = file; break; }\n profiles = [path.join(userHome, \".bashrc\"), login];\n }\n const marker = `# >>> mason PATH ${createHash(\"sha256\").update(bin).digest(\"hex\").slice(0, 12)} >>>`;\n for (const profile of profiles) {\n // Preserve symlinked dotfiles by updating their target. A dangling link is\n // an error, not permission to replace the link with an unrelated file.\n const stat = await fs.lstat(profile).catch(error => { if (error.code === \"ENOENT\") return null; throw error; });\n const file = stat?.isSymbolicLink() ? await fs.realpath(profile) : path.resolve(profile);\n const original = await fs.readFile(file).catch(error => { if (error.code === \"ENOENT\" && !stat) return null; throw error; });\n if (original && !Buffer.from(original.toString(\"utf8\")).equals(original)) return manual(`Shell profile is not UTF-8: ${profile}.`);\n const text = original?.toString(\"utf8\") ?? \"\";\n const owned = changes.find((c): c is Extract<PathChange, { kind: \"profile\" }> => c.kind === \"profile\" && c.file === file);\n if (owned && text.includes(owned.block)) continue;\n if (text.includes(marker) || (owned && text.includes(\"# <<< mason PATH\"))) return manual(`Mason's PATH block was edited; retained ${profile}.`);\n const newline = text.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n const block = owned?.block ?? (\"\\n\" + marker + \"\\ncase \\\":${PATH-}:\\\" in\\n *:\" + quote(bin) + \":*) ;;\\n *) export PATH=\" + quote(bin) + \":\\\"${PATH-}\\\" ;;\\nesac\\n# <<< mason PATH <<<\\n\").replaceAll(\"\\n\", newline);\n if (!owned) { changes.push({ kind: \"profile\", file, block, created: original === null }); await save(changes); }\n await fs.mkdir(path.dirname(file), { recursive: true });\n // Append without rewriting existing bytes, modes or symlinks. Recheck the\n // bytes on the opened file to detect edits since the preflight read.\n const handle = await fs.open(file, original === null ? \"ax+\" : \"a+\", 0o600);\n try {\n if (!(await handle.readFile()).equals(original ?? Buffer.alloc(0))) throw new Error(`Shell profile changed during installation: ${profile}`);\n await handle.writeFile(block); await handle.sync();\n } finally { await handle.close(); }\n }\n return { status: \"configured\", changes, message };\n } catch (error) { return manual(`Could not configure PATH: ${error instanceof Error ? error.message : String(error)}`); }\n}\n\nexport async function removePath(changes: PathChange[]): Promise<string[]> {\n const warnings: string[] = [];\n for (const change of changes) {\n try {\n if (change.kind === \"windows\") {\n const current = await windowsPath(\"read\");\n if (!(current.value ?? \"\").split(\";\").includes(change.entry) && hasWindowsEntry(current.value, change.entry)) throw new Error(\"PATH entry was edited\");\n await windowsPath(\"remove\", change.entry, current, change.created); continue;\n }\n const stat = await fs.lstat(change.file).catch(error => { if (error.code === \"ENOENT\") return null; throw error; });\n if (!stat) continue;\n if (!stat.isFile()) throw new Error(\"profile type changed\");\n const bytes = await fs.readFile(change.file), text = bytes.toString(\"utf8\");\n if (!Buffer.from(text).equals(bytes)) throw new Error(\"profile encoding changed\");\n const index = text.indexOf(change.block);\n if (index < 0 || text.indexOf(change.block, index + change.block.length) >= 0) throw new Error(\"PATH block was edited or removed\");\n const prefix = text.slice(0, index), suffix = text.slice(index + change.block.length);\n const separator = prefix && suffix && !prefix.endsWith(\"\\n\") ? (change.block.startsWith(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\") : \"\";\n const next = Buffer.from(prefix + separator + suffix);\n const temporary = change.file + \".mason-\" + randomUUID();\n try {\n if (stat.nlink > 1) throw new Error(\"profile has multiple hard links\");\n const handle = await fs.open(temporary, \"wx\", stat.mode & 0o777);\n try {\n await handle.writeFile(next); await handle.chmod(stat.mode & 0o777);\n if (process.platform !== \"win32\") await handle.chown(stat.uid, stat.gid);\n await handle.sync();\n } finally { await handle.close(); }\n const current = await fs.lstat(change.file);\n if (!current.isFile() || current.ino !== stat.ino || !(await fs.readFile(change.file)).equals(bytes)) throw new Error(\"profile changed during uninstall\");\n await fs.rename(temporary, change.file);\n } finally { await fs.rm(temporary, { force: true }); }\n // Empty created profiles are harmless; retain them to avoid masking any\n // dotfile another process created or linked since installation.\n } catch (error) { warnings.push(`Retained PATH setting ${change.kind === \"profile\" ? change.file : change.entry}: ${error instanceof Error ? error.message : String(error)}`); }\n }\n return warnings;\n}\n\nexport type WindowsPath = { value: string | null; kind: string | null };\nconst normalizeWindowsEntry = (value: string) => path.win32.normalize(value.trim().replace(/^\"(.*)\"$/, \"$1\")).replace(/[\\\\/]+$/, \"\").toLowerCase();\nexport const hasWindowsEntry = (value: string | null, entry: string, env: NodeJS.ProcessEnv = process.env) => {\n const variables = new Map(Object.entries(env).map(([key, value]) => [key.toLowerCase(), value]));\n const expand = (part: string) => part.replace(/%([^%]+)%/g, (match, key: string) => variables.get(key.toLowerCase()) ?? match);\n return (value ?? \"\").split(\";\").some(part => normalizeWindowsEntry(expand(part)) === normalizeWindowsEntry(entry));\n};\n\n// Read unexpanded registry text and preserve its value kind. No machine PATH,\n// setx truncation, or expanded copy of the process environment is written.\nexport async function windowsPath(action: \"read\" | \"add\" | \"remove\", entry = \"\", expected?: WindowsPath, removeEmpty = false): Promise<WindowsPath> {\n const payload = Buffer.from(JSON.stringify({ action, entry, expected, removeEmpty })).toString(\"base64\");\n const script = `$ErrorActionPreference = 'Stop'\n[Console]::OutputEncoding = New-Object Text.UTF8Encoding($false)\n$p = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${payload}')) | ConvertFrom-Json\n$key = [Microsoft.Win32.Registry]::CurrentUser.CreateSubKey('Environment')\ntry {\n $value = $key.GetValue('Path', $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)\n $kind = if ($null -eq $value) { $null } else { $key.GetValueKind('Path').ToString() }\n if ($null -ne $kind -and $kind -notin @('String','ExpandString')) { throw 'User PATH is not a string.' }\n if ($p.action -eq 'read') { @{ value = $value; kind = $kind } | ConvertTo-Json -Compress; exit 0 }\n if ($p.expected -and ($value -cne $p.expected.value -or $kind -cne $p.expected.kind)) { throw 'User PATH changed concurrently; retry the operation.' }\n $parts = if ($null -eq $value -or $value -eq '') { @() } else { @($value.Split(';')) }\n if ($p.action -eq 'add') { $next = (@($p.entry) + $parts) -join ';' }\n else { $next = (@($parts | Where-Object { $_ -cne $p.entry })) -join ';' }\n if ($next -cne $value) {\n $writeKind = if ($kind) { [Microsoft.Win32.RegistryValueKind]$kind } else { [Microsoft.Win32.RegistryValueKind]::ExpandString }\n if ($p.action -eq 'remove' -and $p.removeEmpty -and $next -eq '') { $key.DeleteValue('Path', $false) }\n else { $key.SetValue('Path', $next, $writeKind) }\n Add-Type -TypeDefinition 'using System; using System.Runtime.InteropServices; public class MasonEnvironment { [DllImport(\"user32.dll\", CharSet=CharSet.Unicode, SetLastError=true)] public static extern IntPtr SendMessageTimeout(IntPtr h, uint m, UIntPtr w, string l, uint f, uint t, out UIntPtr r); }'\n $result = [UIntPtr]::Zero\n [void][MasonEnvironment]::SendMessageTimeout([IntPtr]0xffff, 0x1a, [UIntPtr]::Zero, 'Environment', 2, 1000, [ref]$result)\n }\n @{ value = $next; kind = $kind } | ConvertTo-Json -Compress\n} finally { $key.Dispose() }\n`;\n const { stdout } = await execute(\"powershell.exe\", [\"-NoProfile\", \"-NonInteractive\", \"-ExecutionPolicy\", \"Bypass\", \"-EncodedCommand\", Buffer.from(script, \"utf16le\").toString(\"base64\")], { windowsHide: true, timeout: 15000, maxBuffer: 1024 * 1024 });\n return JSON.parse(stdout.replace(/^\\uFEFF/, \"\").trim());\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\nimport { storePath } from \"../utils/storage.js\";\n\nexport const targets = [\"darwin-arm64\", \"darwin-x64\", \"linux-arm64\", \"linux-x64\", \"win32-arm64\", \"win32-x64\"] as const;\nconst digest = z.string().regex(/^[a-f0-9]{64}$/);\nexport const bundleSchema = z.object({\n format: z.literal(1), version: z.string().regex(/^\\d+\\.\\d+\\.\\d+(?:-[A-Za-z0-9.-]+)?$/),\n target: z.enum(targets), nodeVersion: z.string(), files: z.record(digest),\n}).strict();\nexport const sha256 = (bytes: Buffer | string) => createHash(\"sha256\").update(bytes).digest(\"hex\");\n\nexport async function readBundle(root: string) {\n const bytes = await fs.readFile(await storePath(root, \"bundle.json\"));\n const manifest = bundleSchema.parse(JSON.parse(bytes.toString(\"utf8\")));\n const windows = manifest.target.startsWith(\"win32-\");\n for (const file of Object.keys(manifest.files)) {\n if (file.includes(\"\\\\\") || file.split(\"/\").some(p => !p || p === \".\" || p === \"..\") || path.isAbsolute(file)) {\n throw new Error(\"Unsafe path in Mason bundle: \" + file);\n }\n }\n for (const required of [windows ? \"node.exe\" : \"node\", \"app/package.json\", \"app/dist/mason.js\", \"app/dist/mason-mcp.js\"]) {\n if (!manifest.files[required]) throw new Error(\"Incomplete Mason bundle: \" + required);\n }\n return { manifest, manifestHash: sha256(bytes) };\n}\n\n/** Verify every shipped file before copying or selecting a standalone distribution. */\nexport async function verifyBundle(root: string, expectedHash?: string) {\n const bundle = await readBundle(root);\n if (expectedHash && bundle.manifestHash !== expectedHash) throw new Error(\"Mason bundle manifest changed.\");\n if (bundle.manifest.target !== `${process.platform}-${process.arch}`) throw new Error(\"Mason bundle is for another platform. Install this platform's bundle and rerun setup.\");\n const files = Object.keys(bundle.manifest.files);\n // Bound concurrent reads; Node itself is a large file.\n for (let i = 0; i < files.length; i += 8) await Promise.all(files.slice(i, i + 8).map(async file => {\n if (sha256(await fs.readFile(await storePath(root, file))) !== bundle.manifest.files[file]) throw new Error(\"Mason bundle checksum mismatch: \" + file);\n }));\n return bundle;\n}\n","import tls from \"node:tls\";\n\n/** Called by standalone startup before any application network requests. */\nexport function configureSystemTrust() {\n if (process.env.NODE_USE_SYSTEM_CA === \"0\") return;\n // Older runtimes may lack these APIs; the bundled Node supports both.\n if (typeof tls.getCACertificates !== \"function\" || typeof tls.setDefaultCACertificates !== \"function\") return;\n tls.setDefaultCACertificates([\n ...tls.getCACertificates(\"default\"),\n ...tls.getCACertificates(\"system\"),\n ]);\n}\n\nexport function releaseRequestError(error: unknown): Error {\n const cause = error instanceof Error ? error.cause : undefined;\n const code = cause && typeof cause === \"object\" && \"code\" in cause ? String(cause.code) : undefined;\n const detail = code ?? (error instanceof Error ? error.message : String(error));\n const certificateError = code && /CERT|SELF_SIGNED|UNABLE_TO_VERIFY_LEAF_SIGNATURE/.test(code);\n const hint = certificateError\n ? \" Check that your company's CA certificate is trusted by your operating system, or provide its PEM file with NODE_EXTRA_CA_CERTS.\"\n : \" Check network and proxy access to GitHub.\";\n return new Error(`Could not check the latest Mason release on GitHub (${detail}).${hint}`, { cause: error });\n}\n"],"mappings":"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","names":["fs","os","path","randomUUID","z","z","fs","fs","path","path","fs","path","fs","z","z","z","z","z","os","path","fs","z","fs","os","path","execFile","promisify","createHash","randomUUID","z","execute","z","fs","path","fs","path","createHash","z","digest","z","z","randomUUID","fs","os","path"]}
1
+ {"version":3,"sources":["../src/distribution/runtime.ts","../src/automation/store.ts","../src/utils/store-lock.ts","../src/utils/storage.ts","../src/utils/paths.ts","../src/utils/git-read.ts","../src/utils/profile.ts","../src/utils/files.ts","../src/automation/stop-review.ts","../src/decisions/anchors.ts","../src/audit/inspection.ts","../src/audit/findings.ts","../src/automation/notifications.ts","../src/distribution/state.ts","../src/distribution/path.ts","../src/distribution/bundle.ts","../src/distribution/trust.ts"],"sourcesContent":["import { hasErrorCode } from \"../utils/validation.js\";\nimport fs from \"node:fs/promises\";\nimport { unlinkSync } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { z } from \"zod\";\nimport { withLock } from \"../automation/store.js\";\nimport { readStoreJson, storePath, writeStoreJson } from \"../utils/storage.js\";\nimport { readInstallation, standaloneLocation } from \"./state.js\";\nimport { verifyBundle } from \"./bundle.js\";\nimport { configureSystemTrust } from \"./trust.js\";\n\nconst leaseSchema = z.object({\n pid: z.number().int().positive(),\n host: z.string(),\n bundle: z.string().regex(/^[a-f0-9]{24}$/),\n});\n\n/** Every standalone entry point registers before executing application code. */\nexport async function registerRuntime() {\n const location = standaloneLocation();\n if (!location) return;\n configureSystemTrust();\n const { home, bundle } = location;\n const name = `runtimes/${randomUUID()}.json`;\n await withLock(home, \".install-lock\", async () => {\n await readInstallation(home);\n await fs.access(bundle);\n await writeStoreJson(home, name, {\n pid: process.pid,\n host: os.hostname(),\n bundle: path.basename(bundle),\n });\n });\n const file = await storePath(home, name);\n process.once(\"exit\", () => {\n try {\n unlinkSync(file);\n } catch {\n /* Stale leases are recovered by the next worker. */\n }\n });\n}\n\n/** Caller holds the update lock, before staging starts; startup shares the install lock. */\nexport async function pruneUnusedVersions(home: string) {\n // Full bundle verification and recursive deletion can be slow. Keep them\n // outside the lock needed by commands and new MCP sessions.\n const versions = await storePath(home, \"versions\");\n const candidates: { id: string; directory: string; retired: boolean }[] = [];\n const initial = await readInstallation(home);\n const initialKeep = new Set([initial.current, initial.previous?.id, initial.pending?.id]);\n for (const name of await fs.readdir(versions)) {\n const retired = /^\\.retired-([a-f0-9]{24})-[a-f0-9-]+$/.exec(name);\n const id = retired?.[1] ?? name;\n if (!/^[a-f0-9]{24}$/.test(id) || initialKeep.has(id)) continue;\n try {\n const directory = await storePath(home, `versions/${name}`);\n await fs.access(await storePath(directory, \"app/dist/mason-runtime.js\"));\n const bundle = await verifyBundle(directory);\n if (\n bundle.manifestHash.slice(0, 24) === id &&\n bundle.manifest.files[\"app/dist/mason-runtime.js\"]\n )\n candidates.push({ id, directory, retired: !!retired });\n } catch {\n /* Legacy, edited, or unknown bundles remain untouched. */\n }\n }\n if (!candidates.length) return;\n const garbage: string[] = [];\n await withLock(home, \".install-lock\", async () => {\n const record = await readInstallation(home);\n const keep = new Set([record.current, record.previous?.id, record.pending?.id]);\n const directory = await storePath(home, \"runtimes\");\n const leases = await fs.readdir(directory).catch((error: unknown) => {\n if (hasErrorCode(error, \"ENOENT\")) return [];\n throw error;\n });\n for (const name of leases) {\n if (!/^[a-f0-9-]+\\.json$/.test(name)) return;\n try {\n const raw = await readStoreJson(home, `runtimes/${name}`);\n if (raw === null) continue;\n const lease = leaseSchema.parse(raw);\n if (lease.host !== os.hostname()) return;\n try {\n process.kill(lease.pid, 0);\n keep.add(lease.bundle);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ESRCH\") return;\n await fs.rm(await storePath(home, `runtimes/${name}`), { force: true });\n }\n } catch {\n return;\n } // Uncertain process ownership means no deletion.\n }\n for (const candidate of candidates) {\n if (keep.has(candidate.id)) continue;\n try {\n const retired = candidate.retired\n ? candidate.directory\n : await storePath(home, `versions/.retired-${candidate.id}-${randomUUID()}`);\n if (!candidate.retired) await fs.rename(candidate.directory, retired);\n garbage.push(retired);\n } catch {\n /* Busy files remain available for the next check. */\n }\n }\n });\n for (const directory of garbage) await fs.rm(directory, { recursive: true }).catch(() => {});\n}\n","import { z } from \"zod\";\nimport { withStoreLock } from \"../utils/store-lock.js\";\nimport { stopReviewSchema } from \"./stop-review.js\";\nimport { notificationStateSchema } from \"./notifications.js\";\n\nexport const hostSchema = z.enum([\"claude\", \"codex\"]);\nexport type Host = z.infer<typeof hostSchema>;\nexport const events = [\n \"session_start\",\n \"turn_start\",\n \"before_tool\",\n \"after_tool\",\n \"task_end\",\n] as const;\nexport type Event = (typeof events)[number];\nexport const stateSchema = z.object({\n version: z.literal(1),\n root: z.string(),\n gitDir: z.string(),\n branch: z.string(),\n baselines: z\n .array(\n z.object({ path: z.string(), at: z.string(), event: z.string(), fingerprint: z.string() }),\n )\n .max(128),\n sessions: z.record(\n z.object({\n host: hostSchema,\n lastUsed: z.string(),\n notifiedDiagnostics: z.array(z.string()).default([]),\n pending: z.record(z.string()),\n stopReview: stopReviewSchema.optional(),\n coverageGaps: z.array(z.string()),\n events: z.record(z.object({ at: z.string(), count: z.number().int().positive() })),\n }),\n ),\n updatedAt: z.string(),\n fingerprint: z.string().nullable(),\n latest: z.string().nullable(),\n guidance: z.object({ fingerprint: z.string(), path: z.string() }).optional(),\n notifications: notificationStateSchema.optional(),\n agentNotifications: notificationStateSchema.optional(),\n analysis: z\n .object({ id: z.string(), fingerprint: z.string(), completedAt: z.number(), path: z.string() })\n .optional(),\n});\nexport type State = z.infer<typeof stateSchema>;\nexport function parseState(raw: unknown): State {\n try {\n return stateSchema.parse(raw);\n } catch (error) {\n throw new Error(\"Invalid automation state; original evidence was retained.\", { cause: error });\n }\n}\n\n/** Preserve the automation lock location and diagnostics for existing callers. */\nexport function withLock<T>(\n root: string,\n directory: string,\n run: () => Promise<T>,\n waitMs = 5000,\n): Promise<T> {\n return withStoreLock(root, directory, run, waitMs, \"Automation\");\n}\n","import { z } from \"zod\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport { storePath } from \"./storage.js\";\n\nconst lockOwnerSchema = z.object({ host: z.string(), pid: z.number().int().positive() });\n\nasync function lockDiagnostic(file: string): Promise<string> {\n let reason: string;\n try {\n const owner = lockOwnerSchema.safeParse(JSON.parse(await fs.readFile(file, \"utf8\")));\n if (!owner.success) {\n reason = \"Lock owner data is incomplete or malformed.\";\n } else if (owner.data.host !== os.hostname()) {\n reason = \"The lock belongs to another host; its process cannot be checked locally.\";\n } else {\n try {\n process.kill(owner.data.pid, 0);\n reason = `Local owner PID ${owner.data.pid} is still running.`;\n } catch (error) {\n reason =\n (error as NodeJS.ErrnoException).code === \"ESRCH\"\n ? `Local owner PID ${owner.data.pid} has exited, but lock recovery has not completed.`\n : `Local owner PID ${owner.data.pid} could not be checked.`;\n }\n }\n } catch (error) {\n reason =\n error instanceof SyntaxError\n ? \"Lock owner data is incomplete or malformed.\"\n : \"Lock owner data could not be read; the lock may have changed.\";\n }\n if (\n await fs.lstat(file + \".reclaim\").then(\n () => true,\n () => false,\n )\n ) {\n reason += \" A recovery guard also needs inspection: \" + file + \".reclaim.\";\n }\n return (\n reason +\n \" Retry if a writer is active. For manual recovery, stop all Mason writers for this checkout, confirm they have exited (including other hosts), then remove only confirmed abandoned lock and lock.reclaim files. See docs/reference.md#snapshot-repairs-and-verification.\"\n );\n}\n\n/** Cross-process lock: a killed writer's lock is reclaimed only after its local PID is gone. */\nexport async function withStoreLock<T>(\n root: string,\n directory: string,\n run: () => Promise<T>,\n waitMs = 5000,\n label = \"Mason store\",\n): Promise<T> {\n const file = await storePath(root, directory + \"/lock\", true);\n const deadline = Date.now() + waitMs;\n let handle;\n while (!handle) {\n try {\n handle = await fs.open(file, \"wx\", 0o600);\n } catch (error) {\n // Windows can deny opening a lock while another process finishes deleting it.\n // Retry acquisition only, retaining the original error if access stays denied.\n if (process.platform === \"win32\" && (error as NodeJS.ErrnoException).code === \"EPERM\") {\n if (Date.now() >= deadline) throw error;\n await new Promise((resolve) => setTimeout(resolve, 40));\n continue;\n }\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n // Do not steal malformed, remote, or live locks on a time-based guess.\n try {\n const owner = lockOwnerSchema.parse(JSON.parse(await fs.readFile(file, \"utf8\")));\n if (owner.host === os.hostname() && Number.isInteger(owner.pid) && owner.pid > 0) {\n try {\n process.kill(owner.pid, 0);\n } catch (probe) {\n if ((probe as NodeJS.ErrnoException).code === \"ESRCH\") {\n // A separate guard arbitrates reclamation; recheck the owner before unlinking.\n const reclaim = file + \".reclaim\";\n let guard;\n try {\n guard = await fs.open(reclaim, \"wx\", 0o600);\n const current = lockOwnerSchema.parse(JSON.parse(await fs.readFile(file, \"utf8\")));\n if (current.pid === owner.pid && current.host === owner.host) await fs.unlink(file);\n } finally {\n if (guard) {\n await guard.close();\n await fs.rm(reclaim, { force: true });\n }\n }\n }\n }\n }\n } catch {\n /* Another writer may be creating/releasing/reclaiming it. */\n }\n if (Date.now() >= deadline)\n throw new Error(\n label +\n \" is busy or its lock needs inspection: \" +\n file +\n \". \" +\n (await lockDiagnostic(file)),\n { cause: error },\n );\n await new Promise((resolve) => setTimeout(resolve, 40));\n }\n if (handle) {\n try {\n await handle.writeFile(JSON.stringify({ pid: process.pid, host: os.hostname() }));\n } catch (error) {\n await handle.close().catch(() => {});\n await fs.rm(file, { force: true }).catch(() => {});\n throw error;\n }\n }\n }\n try {\n return await run();\n } finally {\n await handle.close();\n await fs.unlink(file);\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { normalizeRepoPath } from \"./paths.js\";\nimport { readBoundedFile } from \"./files.js\";\n\nexport interface StoreDiagnostic {\n path: string;\n message: string;\n}\n\n/** Metadata paths may not contain symlinks, including their parent directories. */\nexport async function storePath(\n root: string,\n relative: string,\n createParents = false,\n): Promise<string> {\n const normalized = normalizeRepoPath(relative);\n if (!normalized) throw new Error(`Invalid store path: ${relative}`);\n let current = await fs.realpath(root);\n const parts = normalized.split(\"/\");\n for (let i = 0; i < parts.length; i++) {\n current = path.join(current, parts[i]);\n let stat;\n try {\n stat = await fs.lstat(current);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n if (createParents && i < parts.length - 1) {\n try {\n await fs.mkdir(current);\n } catch (mkdirError) {\n if ((mkdirError as NodeJS.ErrnoException).code !== \"EEXIST\") throw mkdirError;\n }\n stat = await fs.lstat(current);\n }\n }\n if (stat?.isSymbolicLink()) throw new Error(`Symlink in store path: ${relative}`);\n }\n return current;\n}\n\nexport async function readStoreJson(root: string, relative: string): Promise<unknown | null> {\n try {\n const file = await storePath(root, relative);\n const raw = await readBoundedFile(file, 10 * 1024 * 1024);\n if (raw === null) throw new Error(\"file is not regular or exceeds 10 MiB\");\n const parsed: unknown = JSON.parse(raw);\n if (parsed === null) throw new Error(\"expected a JSON object, received null\");\n return parsed;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw new Error(\n `Invalid Mason store ${relative}: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n}\n\nexport async function writeStoreJson(\n root: string,\n relative: string,\n value: unknown,\n): Promise<void> {\n const payload = JSON.stringify(value, null, 2) + \"\\n\";\n if (Buffer.byteLength(payload) > 10 * 1024 * 1024) {\n throw new Error(`Mason store ${relative} exceeds 10 MiB`);\n }\n const file = await storePath(root, relative, true);\n const temporary = path.join(path.dirname(file), `.${path.basename(file)}.${randomUUID()}.tmp`);\n try {\n const handle = await fs.open(temporary, \"wx\", 0o600);\n try {\n await handle.writeFile(payload, \"utf8\");\n await handle.sync();\n } finally {\n await handle.close();\n }\n await fs.rename(temporary, file);\n } finally {\n await fs.rm(temporary, { force: true });\n }\n}\n","import path from \"node:path\";\n\n/** One canonical representation for stored paths and decision anchors. */\nexport function normalizeRepoPath(value: string): string | null {\n const slash = value.replace(/\\\\/g, \"/\");\n if (!slash || slash.includes(\"\\0\") || path.posix.isAbsolute(slash) || /^[A-Za-z]:/.test(slash))\n return null;\n if (slash.split(\"/\").includes(\"..\")) return null;\n const normalized = path.posix.normalize(slash).replace(/\\/$/, \"\");\n return normalized === \".\" ? null : normalized;\n}\n\nexport function sanitizeRepoPaths(files: string[]): string[] {\n return [...new Set(files.map(normalizeRepoPath).filter((p): p is string => p !== null))];\n}\n\nexport function isWithinRoot(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative !== \"..\" && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);\n}\n\nexport function anchorMatches(anchor: string, file: string): boolean {\n const a = normalizeRepoPath(anchor);\n const f = normalizeRepoPath(file);\n return a !== null && f !== null && (a === f || f.startsWith(`${a}/`));\n}\n\nexport function matchingPaths(anchors: string[], files: Iterable<string>): string[] {\n return [...new Set(files)].filter((file) =>\n anchors.some((anchor) => anchorMatches(anchor, file)),\n );\n}\n\n/** Match an inventory to every scope in one pass through its path components. */\nexport function indexMatchingPaths(\n scopes: readonly (readonly string[])[],\n files: Iterable<string>,\n): string[][] {\n interface Node {\n children: Map<string, Node>;\n scopes: Set<number>;\n }\n const node = (): Node => ({ children: new Map(), scopes: new Set() });\n const root = node();\n const matches: string[][] = scopes.map(() => []);\n scopes.forEach((anchors, index) => {\n for (const anchor of anchors) {\n const normalized = normalizeRepoPath(anchor);\n if (!normalized) continue;\n let current = root;\n for (const part of normalized.split(\"/\")) {\n if (!current.children.has(part)) current.children.set(part, node());\n current = current.children.get(part)!;\n }\n current.scopes.add(index);\n }\n });\n for (const file of new Set(files)) {\n const normalized = normalizeRepoPath(file);\n if (!normalized) continue;\n const selected = new Set<number>();\n let current: Node | undefined = root;\n for (const part of normalized.split(\"/\")) {\n current = current.children.get(part);\n if (!current) break;\n for (const index of current.scopes) selected.add(index);\n }\n for (const index of selected) matches[index].push(file);\n }\n return matches;\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { profilePhase } from \"./profile.js\";\n\nconst execute = promisify(execFile);\nconst commands = new Set([\n \"rev-parse\",\n \"symbolic-ref\",\n \"for-each-ref\",\n \"ls-files\",\n \"check-ignore\",\n \"status\",\n \"log\",\n \"diff\",\n \"ls-tree\",\n \"show\",\n \"merge-base\",\n]);\nexport interface GitReadOptions {\n cwd: string;\n maxBuffer?: number;\n timeout?: number;\n input?: string;\n}\n\n/** Fixed command labels only: profiling never includes arguments or repository paths. */\nexport function execGit(args: string[], options: GitReadOptions) {\n const command = args.find((arg) => !arg.startsWith(\"-\")) ?? \"other\";\n return profilePhase(\"git.\" + (commands.has(command) ? command : \"other\"), async () => {\n const { input, ...processOptions } = options;\n const operation = execute(\"git\", args, processOptions);\n if (input !== undefined) {\n operation.child.stdin!.on(\"error\", () => {\n /* The child exit reports a failed query. */\n });\n operation.child.stdin!.end(input);\n }\n return operation;\n });\n}\n","import { AsyncLocalStorage } from \"node:async_hooks\";\n\ntype Timing = { calls: number; totalMs: number };\nconst profiles = new AsyncLocalStorage<Record<string, Timing>>();\n\n/** Opt-in, invocation-local timings. Labels are fixed in source; no repository data. */\nexport async function profilePhase<T>(label: string, run: () => Promise<T>): Promise<T> {\n const phases = profiles.getStore();\n if (!phases) return run();\n const start = performance.now();\n try {\n return await run();\n } finally {\n const timing = (phases[label] ??= { calls: 0, totalMs: 0 });\n timing.calls++;\n timing.totalMs += performance.now() - start;\n }\n}\n\nexport async function withProfile<T>(\n run: () => Promise<T>,\n emit: (json: string) => void,\n): Promise<T> {\n const phases: Record<string, Timing> = {};\n const start = performance.now();\n return profiles.run(phases, async () => {\n try {\n return await run();\n } finally {\n emit(\n JSON.stringify({\n kind: \"mason-profile\",\n version: 1,\n totalMs: performance.now() - start,\n phases,\n }),\n );\n }\n });\n}\n","import { isRecord, isStringArray } from \"./validation.js\";\nimport { execGit } from \"./git-read.js\";\nimport fs from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport path from \"node:path\";\nimport fg from \"fast-glob\";\nimport { isWithinRoot, normalizeRepoPath } from \"./paths.js\";\n\nexport const SOURCE_EXTENSIONS = [\n \"ts\",\n \"tsx\",\n \"js\",\n \"jsx\",\n \"mts\",\n \"cts\",\n \"mjs\",\n \"cjs\",\n \"vue\",\n \"svelte\",\n \"kt\",\n \"kts\",\n \"java\",\n \"py\",\n \"go\",\n \"rs\",\n \"swift\",\n \"rb\",\n \"cs\",\n \"cpp\",\n \"c\",\n \"h\",\n \"hpp\",\n \"dart\",\n \"php\",\n];\nexport const SOURCE_GLOB = `**/*.{${SOURCE_EXTENSIONS.join(\",\")}}`;\nexport const SOURCE_IGNORE = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.gradle/**\",\n \"**/target/**\",\n \"**/.git/**\",\n \"**/.mason/**\",\n \"**/vendor/**\",\n \"**/__pycache__/**\",\n \"**/venv/**\",\n \"**/.venv/**\",\n \"**/*.min.*\",\n \"**/*.map\",\n \"**/*.lock\",\n \"**/generated/**\",\n \"**/*.generated.*\",\n \"**/R.java\",\n \"**/BuildConfig.java\",\n \"**/package-lock.json\",\n \"**/yarn.lock\",\n \"**/pnpm-lock.yaml\",\n];\nexport const MAX_SOURCE_BYTES = 1024 * 1024;\nexport interface ProjectConfig {\n patterns?: string[];\n alwaysInclude?: string[];\n ignore?: string[];\n audit?: {\n include?: string[];\n exclude?: Partial<Record<import(\"../audit/types.js\").CheckName, string[]>>;\n };\n}\nexport interface SourceFile {\n path: string;\n content: string;\n totalLines: number;\n}\n\nexport function isSensitiveFile(file: string): boolean {\n return file\n .split(/[\\\\/]/)\n .some((part) =>\n /^(?:\\.env(?:\\..*)?|id_rsa.*|id_ed25519.*)$|\\.(?:pem|key|p12|pfx|jks|keystore)$|credentials\\.|secret|^local\\.properties$/i.test(\n part,\n ),\n );\n}\n\n/** Bound reads even if a file grows after stat. Only read regular files. */\nexport async function readBoundedFile(file: string, maxBytes: number): Promise<string | null> {\n // Do not block on a FIFO or follow a symlink substituted after resolution.\n const handle = await fs.open(\n file,\n constants.O_RDONLY | constants.O_NONBLOCK | constants.O_NOFOLLOW,\n );\n try {\n const stat = await handle.stat();\n if (!stat.isFile() || stat.size > maxBytes) return null;\n const buffer = Buffer.alloc(Math.min(maxBytes + 1, stat.size + 1));\n let bytes = 0;\n while (bytes < buffer.length) {\n const result = await handle.read(buffer, bytes, buffer.length - bytes, null);\n if (result.bytesRead === 0) break;\n bytes += result.bytesRead;\n }\n return bytes === buffer.length ? null : buffer.subarray(0, bytes).toString(\"utf8\");\n } finally {\n await handle.close();\n }\n}\n\nexport async function loadProjectConfig(root: string): Promise<ProjectConfig> {\n try {\n const canonicalRoot = await fs.realpath(root);\n const configPath = await fs.realpath(path.join(root, \".mason/config.json\"));\n if (!isWithinRoot(canonicalRoot, configPath))\n throw new Error(\"Project configuration resolves outside the repository\");\n const raw = await readBoundedFile(configPath, 64 * 1024);\n if (raw === null)\n throw new Error(\"Project configuration is not a regular file or exceeds 64 KiB\");\n const value: unknown = JSON.parse(raw);\n if (!isRecord(value)) throw new Error(\"Expected a configuration object\");\n const config: ProjectConfig = {};\n for (const key of [\"patterns\", \"alwaysInclude\", \"ignore\"] as const) {\n if (value[key] === undefined) continue;\n if (!isStringArray(value[key])) {\n throw new Error(`Configuration ${key} must be an array of strings`);\n }\n config[key] = value[key];\n }\n if (value.audit !== undefined) {\n const audit = value.audit;\n if (!isRecord(audit)) throw new Error(\"Configuration audit must be an object\");\n const patterns = (raw: unknown): string[] => {\n if (\n !isStringArray(raw) ||\n raw.length > 100 ||\n !raw.every(\n (s) =>\n typeof s === \"string\" &&\n normalizeRepoPath(s) === s &&\n !s.startsWith(\"!\") &&\n !/[\\x00-\\x1f]/.test(s),\n )\n ) {\n throw new Error(\n \"Audit patterns must be at most 100 repository-relative globs without traversal or negation\",\n );\n }\n return raw;\n };\n config.audit = {};\n if (audit.include !== undefined) config.audit.include = patterns(audit.include);\n if (audit.exclude !== undefined) {\n if (!isRecord(audit.exclude))\n throw new Error(\"audit.exclude must map check names to patterns\");\n const { ALL_CHECKS } = await import(\"../audit/types.js\");\n config.audit.exclude = {};\n for (const [check, globs] of Object.entries(audit.exclude)) {\n if (!ALL_CHECKS.includes(check as (typeof ALL_CHECKS)[number]))\n throw new Error(\"Unknown audit check: \" + check);\n config.audit.exclude[check as (typeof ALL_CHECKS)[number]] = patterns(globs);\n }\n }\n }\n return config;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return {};\n throw new Error(\n `Cannot apply project file policy: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n}\n\n/** Scoped to one operation, so a later tool call sees newly edited files/ignores. */\nexport async function createFileAccess(rootDir: string) {\n const root = path.resolve(rootDir);\n const canonicalRoot = await fs.realpath(root).catch(() => root);\n const config = await loadProjectConfig(root);\n const ignore = [...SOURCE_IGNORE, ...(config.ignore ?? [])];\n let gitFiles: Set<string> | null = null;\n try {\n const { stdout } = await execGit(\n [\"ls-files\", \"-z\", \"--cached\", \"--others\", \"--exclude-standard\"],\n { cwd: root, maxBuffer: 50 * 1024 * 1024 },\n );\n gitFiles = new Set(stdout.split(\"\\0\").filter(Boolean));\n } catch {\n // File-system projects are supported. Fail closed if this IS a Git repo.\n let inGit = false;\n try {\n await execGit([\"rev-parse\", \"--git-dir\"], { cwd: root });\n inGit = true;\n } catch {\n /* no Git */\n }\n if (inGit) throw new Error(\"Cannot enumerate Git files safely\");\n }\n\n async function resolve(file: string): Promise<string | null> {\n const relative = normalizeRepoPath(file);\n if (!relative || isSensitiveFile(relative) || (gitFiles && !gitFiles.has(relative)))\n return null;\n const candidate = path.join(root, relative);\n try {\n const real = await fs.realpath(candidate);\n if (!isWithinRoot(canonicalRoot, real) || isSensitiveFile(path.relative(canonicalRoot, real)))\n return null;\n const stat = await fs.stat(real);\n if (!stat.isFile() || stat.size > MAX_SOURCE_BYTES) return null;\n // A symlink must not bypass the target's ignore policy either.\n if (gitFiles && !gitFiles.has(path.relative(canonicalRoot, real).split(path.sep).join(\"/\")))\n return null;\n return real;\n } catch {\n return null;\n }\n }\n\n async function list(\n patterns: string | string[] = SOURCE_GLOB,\n options: { deep?: number; dot?: boolean } = {},\n ): Promise<string[]> {\n const found = await fg(patterns, { cwd: root, ignore, followSymbolicLinks: false, ...options });\n const safe = await Promise.all(found.map(async (f) => ((await resolve(f)) ? f : null)));\n return safe.filter((f): f is string => f !== null).sort();\n }\n\n async function read(file: string): Promise<SourceFile | null> {\n const relative = normalizeRepoPath(file);\n if (!relative) return null;\n const real = await resolve(relative);\n if (!real) return null;\n // Apply the same glob exclusions to explicit reads and symlink targets.\n for (const rel of new Set([\n relative,\n path.relative(canonicalRoot, real).split(path.sep).join(\"/\"),\n ])) {\n if (!(await fg(fg.escapePath(rel), { cwd: root, ignore, dot: true })).length) return null;\n }\n try {\n const content = await readBoundedFile(real, MAX_SOURCE_BYTES);\n return content === null\n ? null\n : { path: relative, content, totalLines: content.split(\"\\n\").length };\n } catch {\n return null;\n }\n }\n return { root, config, list, read, inventory: () => (gitFiles ? [...gitFiles].sort() : null) };\n}\n","import { z } from \"zod\";\nimport { anchorCaptureSchema, changedCaptureFiles } from \"../decisions/anchors.js\";\nimport type { Inputs } from \"./evidence.js\";\nimport { digest } from \"../audit/findings.js\";\nimport type { State } from \"./store.js\";\nimport type { AutomationEvent, AutomationReport } from \"./runtime.js\";\n\nconst evidenceSchema = z.record(z.object({ digest: z.string(), capture: anchorCaptureSchema }));\nexport const stopReviewSchema = z.object({\n pending: z.record(z.object({ head: z.string(), evidence: evidenceSchema })),\n edited: z.record(z.record(z.string().nullable())),\n delivered: z.record(z.object({ digest: z.string(), files: z.record(z.string().nullable()) })),\n});\ntype Session = State[\"sessions\"][string];\nconst empty = (): z.infer<typeof stopReviewSchema> => ({ pending: {}, edited: {}, delivered: {} });\n\n/** Only paired captures establish observed edits, independently of commits and dirty status. */\nexport function observeDecisionEdits(session: Session, inputs: Inputs, event: AutomationEvent) {\n if (event.host !== \"claude\" || !event.mutating || !event.toolId) return;\n const state = (session.stopReview ??= empty());\n if (event.event === \"before_tool\") {\n state.pending[event.toolId] = { head: inputs.head, evidence: inputs.decisionEvidence };\n } else if (event.event === \"after_tool\") {\n const before = state.pending[event.toolId];\n delete state.pending[event.toolId];\n // A pull/merge/checkout can change content without a local edit. If HEAD moved,\n // retain earlier observations but do not attribute this interval to the agent.\n if (!before || before.head !== inputs.head) return;\n for (const [id, previous] of Object.entries(before.evidence)) {\n const current = inputs.decisionEvidence[id];\n if (\n !current ||\n !previous.capture.complete ||\n !current.capture.complete ||\n digest(previous.capture.anchors) !== digest(current.capture.anchors)\n )\n continue;\n const edited = (state.edited[id] ??= {});\n for (const file of changedCaptureFiles(previous.capture, current.capture)) {\n edited[file] = current.capture.files[file] ?? null;\n }\n }\n }\n}\n\n/** Delivery is not resolution. Only explicit resolution clears its receipt. */\nexport function clearResolvedStopReviews(sessions: State[\"sessions\"], report: AutomationReport) {\n if (\n report.status === \"unavailable\" ||\n report.checks.skipped.some((check) => check.check === \"decision-anchor-drift\")\n )\n return;\n const active = new Set(\n report.findings.flatMap((finding) => {\n const evidence = (finding.current ?? finding.original).evidence;\n return evidence.kind === \"decision-anchor\" &&\n finding.status !== \"resolved\" &&\n (finding.current || finding.status === \"unverified\")\n ? [evidence.decisionId]\n : [];\n }),\n );\n for (const session of Object.values(sessions)) {\n if (!session.stopReview) continue;\n for (const id of Object.keys(session.stopReview.delivered)) {\n if (!active.has(id)) delete session.stopReview.delivered[id];\n }\n }\n}\n\nexport function decisionStopReview(\n session: Session,\n inputs: Inputs,\n report: AutomationReport,\n reentry: boolean,\n) {\n const state = (session.stopReview ??= empty());\n const decisions = new Map<\n string,\n { title: string; digest: string; files: Record<string, string | null> }\n >();\n for (const finding of report.findings) {\n const evidence = finding.current?.evidence;\n if (\n finding.status === \"resolved\" ||\n finding.status === \"unverified\" ||\n evidence?.kind !== \"decision-anchor\"\n )\n continue;\n const current = inputs.decisionEvidence[evidence.decisionId];\n const edited = state.edited[evidence.decisionId];\n if (\n !current?.capture.complete ||\n !edited ||\n !evidence.changedFiles.some(\n (file) =>\n Object.hasOwn(edited, file) && edited[file] === (current.capture.files[file] ?? null),\n )\n )\n continue;\n const value = decisions.get(evidence.decisionId) ?? {\n title: evidence.title,\n digest: current.digest,\n files: {},\n };\n for (const file of evidence.changedFiles) {\n if (Object.hasOwn(edited, file) && edited[file] === (current.capture.files[file] ?? null))\n value.files[file] = edited[file];\n }\n decisions.set(evidence.decisionId, value);\n }\n if (!decisions.size) return { reason: null, block: false };\n let fresh = false;\n for (const [id, value] of decisions) {\n const previous = state.delivered[id];\n if (\n previous?.digest !== value.digest ||\n Object.entries(value.files).some(\n ([file, digest]) => !Object.hasOwn(previous.files, file) || previous.files[file] !== digest,\n )\n )\n fresh = true;\n // Consume feedback on re-entry too: another hook may have continued the turn.\n state.delivered[id] = {\n digest: value.digest,\n files: { ...(previous?.digest === value.digest ? previous.files : {}), ...value.files },\n };\n }\n const safe = (value: string) => value.replace(/[\\u0000-\\u001f\\u007f-\\u009f]/g, \" \").slice(0, 250);\n const reason = [\n \"Mason: unresolved decision drift overlaps changes observed during this session. Before ending the turn, inspect these decisions:\",\n ...[...decisions].map(\n ([id, value]) =>\n `Decision ${JSON.stringify(safe(value.title))} (${safe(id)}): review_decision(action: \"prepare\", id: ${JSON.stringify(id)}).`,\n ),\n 'Check the claims against the source and diff. When an accepted revision exists, prepare and inspect with inspectionScope: \"operative\" to assess it separately; a contradictory pending draft remains unassessed and must not be promoted. For proposal-only or legacy records use the default all scope. If no contradiction is found in the selected scope, record action: \"inspect\" with the reviewToken, actual inspector, and a note explaining the evidence. \"Expected changes\" or \"my own commits\" are not evidence.',\n \"If contradicted or uncertain, explain the mismatch to the user and request direction. Do not accept, reaffirm, or retire a decision without authorized human review. A revised proposal leaves the accepted revision operative until reviewed.\",\n `Evidence: ${report.reportPath}. Concurrent changes can contribute; captures do not prove authorship. This reminder does not resolve the finding.`,\n ].join(\"\\n\");\n return { reason, block: !reentry && fresh };\n}\n","import { createHash } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport { z } from \"zod\";\nimport fg from \"fast-glob\";\nimport { inspectionGit, inspectionRead } from \"../audit/inspection.js\";\nimport { createFileAccess, MAX_SOURCE_BYTES } from \"../utils/files.js\";\nimport { indexMatchingPaths, matchingPaths, normalizeRepoPath } from \"../utils/paths.js\";\nimport { storePath } from \"../utils/storage.js\";\n\nconst repoPath = z.string().refine((p) => normalizeRepoPath(p) === p);\nexport const anchorCaptureSchema = z\n .object({\n anchors: z.array(repoPath),\n files: z.record(repoPath, z.string().regex(/^[a-f0-9]{64}$/)),\n complete: z.boolean(),\n })\n .refine(\n (capture) =>\n JSON.stringify(capture.anchors) === JSON.stringify([...new Set(capture.anchors)].sort()) &&\n matchingPaths(capture.anchors, Object.keys(capture.files)).length ===\n Object.keys(capture.files).length,\n \"Capture paths must belong to its normalized, sorted anchor scope\",\n );\nexport type AnchorCapture = z.infer<typeof anchorCaptureSchema>;\n\n/** Content evidence, not approval. Unreadable/excluded scopes never become current.\n * Batch independent scopes and share observations only inside one repository inspection.\n * Final validation creates a fresh inspection; standalone callers always read again.\n */\nexport async function captureAnchorScopes(\n root: string,\n scopes: string[][],\n): Promise<AnchorCapture[]> {\n const union = [...new Set(scopes.flat())].sort();\n let context: ReturnType<typeof prepareContext> | undefined;\n const prepareContext = async () => {\n const access = await inspectionRead(root, \"decision-anchor-access\", () =>\n createFileAccess(root),\n );\n const inventory = access.inventory();\n if (!inventory) throw new Error(\"Git anchor inventory unavailable\");\n const ignored = await inspectionGit(\n [\n \"ls-files\",\n \"-z\",\n \"--others\",\n \"--ignored\",\n \"--exclude-standard\",\n \"--\",\n ...union.map((anchor) => `:(literal)${anchor}`),\n ],\n { cwd: root, maxBuffer: 8 * 1024 * 1024 },\n );\n const files = [\n ...new Set([...inventory, ...ignored.stdout.split(\"\\0\").filter(Boolean)]),\n ].sort();\n // Apply file policy in one glob walk rather than one walk per anchor file.\n // storePath still rejects symlinked parents at each bounded byte read.\n const patterns = await Promise.all(\n union.map(async (anchor) => {\n const literal = fg.escapePath(anchor);\n try {\n return (await fs.lstat(await storePath(root, anchor))).isDirectory()\n ? `${literal}/**/*`\n : literal;\n } catch {\n return literal;\n }\n }),\n );\n const eligible = new Set(await access.list(patterns, { dot: true }));\n const indexed = indexMatchingPaths(scopes, files);\n const filesByScope = new Map(\n scopes.map((scope, index) => [JSON.stringify([...new Set(scope)].sort()), indexed[index]]),\n );\n return { filesByScope, eligible };\n };\n return Promise.all(\n scopes.map((scope) => {\n const anchors = [...new Set(scope)].sort();\n return inspectionRead(\n root,\n \"decision-anchor-capture:\" + JSON.stringify(anchors),\n async () => {\n const capture: AnchorCapture = { anchors, files: {}, complete: true };\n if (!anchors.length) return capture;\n try {\n const { eligible, filesByScope } = await (context ??= prepareContext());\n const files = filesByScope.get(JSON.stringify(anchors))!;\n if (files.length > 2000) return { ...capture, complete: false };\n for (const file of files) {\n const evidence = await inspectionRead(\n root,\n \"decision-anchor-file:\" + file,\n async () => {\n try {\n const absolute = await storePath(root, file);\n await fs.lstat(absolute); // Missing indexed files are deletions, not unreadable evidence.\n if (!eligible.has(file)) return { complete: false };\n const handle = await fs.open(\n absolute,\n constants.O_RDONLY | constants.O_NONBLOCK | constants.O_NOFOLLOW,\n );\n try {\n const stat = await handle.stat();\n if (!stat.isFile() || stat.size > MAX_SOURCE_BYTES)\n return { complete: false };\n const bytes = Buffer.alloc(Math.min(stat.size + 1, MAX_SOURCE_BYTES + 1));\n let size = 0;\n while (size < bytes.length) {\n const read = await handle.read(bytes, size, bytes.length - size, null);\n if (!read.bytesRead) break;\n size += read.bytesRead;\n }\n if (size === bytes.length) return { complete: false }; // Grew during capture.\n return {\n complete: true,\n hash: createHash(\"sha256\").update(bytes.subarray(0, size)).digest(\"hex\"),\n };\n } finally {\n await handle.close();\n }\n } catch (error) {\n return { complete: (error as NodeJS.ErrnoException).code === \"ENOENT\" };\n }\n },\n );\n if (!evidence.complete) capture.complete = false;\n if (evidence.hash) capture.files[file] = evidence.hash;\n }\n } catch {\n capture.complete = false;\n }\n return capture;\n },\n );\n }),\n );\n}\n\nexport async function captureAnchors(root: string, anchors: string[]): Promise<AnchorCapture> {\n return (await captureAnchorScopes(root, [anchors]))[0];\n}\n\nexport function changedCaptureFiles(before: AnchorCapture, after: AnchorCapture): string[] {\n return [...new Set([...Object.keys(before.files), ...Object.keys(after.files)])]\n .filter((file) => before.files[file] !== after.files[file])\n .sort();\n}\n","import fg from \"fast-glob\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport path from \"node:path\";\nimport { execGit, type GitReadOptions } from \"../utils/git-read.js\";\nimport { loadProjectConfig, SOURCE_EXTENSIONS } from \"../utils/files.js\";\n\ninterface Inspection {\n root: string;\n reads: Map<string, Promise<unknown>>;\n}\nconst inspections = new AsyncLocalStorage<Inspection>();\n\n/** Always start fresh. In particular, final validation must not reuse initial reads. */\nexport function withRepositoryInspection<T>(root: string, run: () => Promise<T>): Promise<T> {\n return inspections.run({ root: path.resolve(root), reads: new Map() }, run);\n}\n\n/** Read observations are shared only inside an explicit inspection, never across hooks. */\nexport function inspectionRead<T>(root: string, key: string, read: () => Promise<T>): Promise<T> {\n const inspection = inspections.getStore();\n if (!inspection || inspection.root !== path.resolve(root)) return read();\n if (!inspection.reads.has(key)) {\n const pending = read();\n inspection.reads.set(key, pending);\n // A failed query must be retried, not turned into a reusable unavailable result.\n void pending.catch(() => {\n if (inspection.reads.get(key) === pending) inspection.reads.delete(key);\n });\n }\n return inspection.reads.get(key)! as Promise<T>;\n}\n\n/** Tool writes invalidate their cached observations within the active inspection. */\nexport function invalidateInspectionReads(root: string, prefix: string): void {\n const inspection = inspections.getStore();\n if (!inspection || inspection.root !== path.resolve(root)) return;\n for (const key of inspection.reads.keys())\n if (key.startsWith(prefix)) inspection.reads.delete(key);\n}\n\n/** Exact arguments and limits preserve each history query's semantics. */\nexport function inspectionGit(args: string[], options: GitReadOptions) {\n return inspectionRead(options.cwd, \"git:\" + JSON.stringify([args, options]), () =>\n execGit(args, options),\n );\n}\n\nconst sourceSpecs = SOURCE_EXTENSIONS.map((extension) => `:(glob)**/*.${extension}`);\nconst docSpecs = [\n \":(icase,glob)**/readme.md\",\n \":(icase,glob)**/agents.md\",\n \":(icase,glob)**/claude.md\",\n];\nconst sourceSuffixes = new Set(SOURCE_EXTENSIONS.map((extension) => \".\" + extension));\n\n/** Git still prunes ignored trees; unrelated generated files are never inventoried. */\nexport async function auditGitPaths(\n root: string,\n kind: \"documents\" | \"sources\",\n): Promise<string[]> {\n const custom = (await loadProjectConfig(root)).audit?.include ?? [];\n const shared = inspections.getStore()?.root === path.resolve(root) && !custom.length;\n const expanded = fg.generateTasks(custom).flatMap((task) => task.positive);\n if (expanded.length > 1000)\n throw new Error(\"Audit include patterns expand to more than 1,000 globs\");\n const documentSpecs = [...docSpecs, ...expanded.map((pattern) => `:(glob)${pattern}`)];\n const specs = shared\n ? [...documentSpecs, ...sourceSpecs]\n : kind === \"documents\"\n ? documentSpecs\n : sourceSpecs;\n const limit = kind === \"documents\" ? 16 * 1024 * 1024 : 32 * 1024 * 1024;\n const { stdout } = await inspectionGit(\n [\"ls-files\", \"-z\", \"--cached\", \"--others\", \"--exclude-standard\", \"--\", ...specs],\n { cwd: root, maxBuffer: shared ? 48 * 1024 * 1024 : limit, timeout: 10000 },\n );\n const paths = [...new Set(stdout.split(\"\\0\").filter(Boolean))].filter((file) =>\n kind === \"documents\"\n ? custom.length > 0 || /^(readme|agents|claude)\\.md$/i.test(path.posix.basename(file))\n : // Git's *.ts also matches a file named .ts, unlike path.extname().\n sourceSuffixes.has(file.slice(file.lastIndexOf(\".\"))),\n );\n if (Buffer.byteLength(paths.join(\"\\0\")) > limit)\n throw new Error(\"Git \" + kind + \" inventory exceeds its bounded read limit.\");\n return paths.sort();\n}\n","import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\nimport type { AuditIssue, AuditAdvisory } from \"./types.js\";\nexport type Finding = AuditIssue | AuditAdvisory;\nexport const digest = (value: unknown) =>\n createHash(\"sha256\").update(JSON.stringify(value)).digest(\"hex\");\nexport const checkSchema = z.enum([\n \"deleted-reference\",\n \"new-module\",\n \"stale-count\",\n \"dead-command\",\n \"deps-changed\",\n \"decision-anchor-drift\",\n]);\nexport const commitSchema = z.object({\n hash: z.string().regex(/^[a-f0-9]{40,64}$/),\n date: z.string(),\n subject: z.string(),\n});\nconst anchorSchema = z.object({\n doc: z.string(),\n line: z.number().int().positive().nullable(),\n excerpt: z.string().nullable(),\n});\nconst count = z.number().int().nonnegative();\nconst scopeSchema = z.object({\n basis: z.enum([\"repository\", \"document\", \"document-link\", \"explicit\"]),\n directory: z.string(),\n candidates: z.array(z.string()),\n});\nconst evidenceSchema = z.discriminatedUnion(\"kind\", [\n z.object({\n kind: z.literal(\"missing-path\"),\n claimed: z.string(),\n renamedTo: z.string().nullable(),\n deletedInCommit: commitSchema.nullable(),\n everTracked: z.boolean(),\n parentDirExists: z.boolean(),\n resolvedPath: z.string().optional(),\n scope: scopeSchema.optional(),\n }),\n z.object({\n kind: z.literal(\"unmentioned-dir\"),\n dir: z.string(),\n sourceFileCount: count,\n firstCommit: commitSchema.nullable(),\n checkedDocs: z.array(z.string()),\n }),\n z.object({\n kind: z.literal(\"count-mismatch\"),\n claimed: count,\n actual: count,\n unit: z.string(),\n countedFrom: z.string(),\n members: z.array(z.string()),\n }),\n z.object({\n kind: z.literal(\"missing-script\"),\n scriptName: z.string(),\n invocation: z.string(),\n manifestsChecked: z.array(z.string()),\n availableScripts: z.array(z.string()),\n scope: scopeSchema.optional(),\n }),\n z.object({\n kind: z.literal(\"doc-behind-manifests\"),\n docLastCommit: commitSchema,\n manifestCommits: z.array(commitSchema.extend({ files: z.array(z.string()) })),\n totalCommits: count,\n matches: z\n .array(\n z.object({\n dependency: z.string(),\n manifest: z.string(),\n before: z.string().nullable(),\n after: z.string().nullable(),\n line: z.number().int().positive(),\n excerpt: z.string(),\n }),\n )\n .optional(),\n matchingIncomplete: z.boolean().optional(),\n }),\n z.object({\n kind: z.literal(\"decision-anchor\"),\n decisionId: z.string(),\n title: z.string(),\n changedFiles: z.array(z.string()),\n refreshedHash: z.string(),\n provenance: z.object({}).passthrough().optional(),\n }),\n]);\nconst findingSchema = z.object({\n message: z.string(),\n anchor: anchorSchema,\n evidence: evidenceSchema,\n});\nexport const issueSchema = findingSchema.extend({\n type: z.enum([\"deleted-reference\", \"new-module\", \"stale-count\", \"dead-command\"]),\n confidence: z.enum([\"certain\", \"likely\"]),\n});\nexport const advisorySchema = findingSchema.extend({\n type: checkSchema,\n resolution: z.enum([\"recheck\", \"informational\"]).optional(),\n});\nexport const checkResultSchema = z.object({\n issues: z.array(issueSchema),\n advisories: z.array(advisorySchema),\n suppressedAdvisories: z.array(advisorySchema).optional(),\n skipped: z.array(z.object({ check: z.string(), reason: z.string(), doc: z.string().optional() })),\n});\n/** Lines and wording can change without changing the underlying claim. */\nexport function findingId(finding: Finding): string {\n const e = finding.evidence;\n let key: unknown;\n switch (e.kind) {\n case \"missing-path\":\n key =\n e.resolvedPath && e.resolvedPath !== e.claimed ? [e.claimed, e.resolvedPath] : e.claimed;\n break;\n case \"unmentioned-dir\":\n key = e.dir;\n break;\n case \"count-mismatch\":\n key = [e.unit.replace(/s$/, \"\"), e.countedFrom];\n break;\n case \"missing-script\":\n key = e.scope && e.scope.directory !== \".\" ? [e.scriptName, e.scope.directory] : e.scriptName;\n break;\n case \"doc-behind-manifests\":\n key = null;\n break;\n case \"decision-anchor\":\n key = [e.decisionId, e.provenance?.revision, e.provenance?.approval];\n break;\n }\n return digest([finding.type, finding.anchor.doc, key]);\n}\n","import { z } from \"zod\";\nimport type { RepairFinding } from \"../audit/repair.js\";\nimport { digest } from \"../audit/findings.js\";\n\n// Compare conditions, not report wording, line numbers, commit IDs, or timestamps.\nconst conditionSchema = z.object({\n level: z.number(),\n facts: z.array(z.string()),\n magnitude: z.number().optional(),\n});\nconst MAX_COMPLETION_FINDINGS = 4;\n\ntype Condition = z.infer<typeof conditionSchema>;\nexport const notificationStateSchema = z.object({\n version: z.literal(1),\n baselineHead: z.string(),\n baseline: z.record(conditionSchema),\n delivered: z.record(conditionSchema),\n cleared: z.array(z.string()),\n});\nexport type NotificationState = z.infer<typeof notificationStateSchema>;\n\nfunction condition(finding: RepairFinding): { id: string; condition: Condition } {\n const source = finding.current ?? finding.original;\n const e = source.evidence;\n // Decision approval/revision changes alone do not create a new drift condition.\n const id =\n e.kind === \"decision-anchor\"\n ? digest([source.type, source.anchor.doc, e.decisionId])\n : finding.id;\n const result: Condition = { level: \"confidence\" in source ? 2 : 1, facts: [] };\n switch (e.kind) {\n case \"count-mismatch\":\n result.magnitude = Math.abs(e.actual - e.claimed);\n break;\n case \"unmentioned-dir\":\n result.magnitude = e.sourceFileCount;\n break;\n case \"decision-anchor\":\n result.facts = [...e.changedFiles].sort();\n break;\n case \"doc-behind-manifests\":\n result.facts = (e.matches ?? [])\n .map((m) => JSON.stringify([m.manifest, m.dependency, m.after]))\n .sort();\n break;\n // For missing paths/scripts, the finding ID already identifies the condition.\n }\n return { id, condition: result };\n}\n\nexport function notificationConditions(findings: RepairFinding[]): Record<string, Condition> {\n const active = findings.filter((f) => f.status !== \"resolved\");\n const currentIds = new Set(active.filter((f) => f.current).map((f) => condition(f).id));\n const result: Record<string, Condition> = {};\n for (const f of active) {\n const value = condition(f);\n // Historical revisions must not override current evidence for the same decision.\n if (!f.current && currentIds.has(value.id)) continue;\n const previous = result[value.id];\n result[value.id] = previous\n ? {\n level: Math.max(previous.level, value.condition.level),\n facts: [...new Set([...previous.facts, ...value.condition.facts])].sort(),\n ...(previous.magnitude !== undefined || value.condition.magnitude !== undefined\n ? { magnitude: Math.max(previous.magnitude ?? 0, value.condition.magnitude ?? 0) }\n : {}),\n }\n : value.condition;\n }\n return result;\n}\n\nexport function createNotificationState(\n findings: RepairFinding[],\n head: string,\n): NotificationState {\n return {\n version: 1,\n baselineHead: head,\n baseline: notificationConditions(findings),\n delivered: {},\n cleared: [],\n };\n}\n\nfunction worsened(current: Condition, previous?: Condition): boolean {\n return (\n !previous ||\n current.level > previous.level ||\n (current.magnitude ?? 0) > (previous.magnitude ?? 0) ||\n current.facts.some((fact) => !previous.facts.includes(fact))\n );\n}\n\n/** Each delivery channel owns its state; explicit audits never consume notifications. */\nexport function completionFindings(\n state: NotificationState,\n findings: RepairFinding[],\n): RepairFinding[] {\n const current = notificationConditions(findings);\n const cleared = new Set(state.cleared);\n const resolvedIds = new Set(\n findings.filter((f) => f.status === \"resolved\").map((f) => condition(f).id),\n );\n for (const id of Object.keys(state.baseline))\n if (resolvedIds.has(id) && !current[id]) cleared.add(id);\n state.cleared = [...cleared];\n\n const selected: RepairFinding[] = [];\n const selectedIds = new Set<string>();\n const delivered: Record<string, Condition> = { ...state.delivered };\n for (const finding of findings) {\n const value = condition(finding);\n const previous = state.delivered[value.id];\n value.condition = current[value.id] ?? value.condition;\n // Missing or temporarily unverified evidence must not erase delivery history.\n if (finding.status === \"unverified\" || (!finding.current && finding.status !== \"resolved\")) {\n continue;\n }\n if (finding.status === \"resolved\") {\n if (!current[value.id]) delete delivered[value.id];\n continue;\n }\n const eligible = cleared.has(value.id) || worsened(value.condition, state.baseline[value.id]);\n if (!eligible) continue;\n if (!selectedIds.has(value.id) && worsened(value.condition, previous)) {\n selectedIds.add(value.id);\n const source = finding.current!;\n selected.push(\n source.evidence.kind === \"decision-anchor\"\n ? {\n ...finding,\n current: {\n ...source,\n evidence: { ...source.evidence, changedFiles: value.condition.facts },\n },\n }\n : finding,\n );\n // Only displayed findings count as delivered. Overflow remains eligible\n // for the next completion, including after a session restart.\n if (selected.length <= MAX_COMPLETION_FINDINGS) delivered[value.id] = value.condition;\n }\n }\n state.delivered = delivered;\n return selected;\n}\n\nexport function completionSummary(findings: RepairFinding[], reportPath: string): string | null {\n if (!findings.length) return null;\n const safe = (value: string) => value.replace(/[\\u0000-\\u001f\\u007f-\\u009f]/g, \" \").slice(0, 250);\n return [\n `Mason: ${findings.length} new or worsened finding(s) since the retained baseline.`,\n ...findings.slice(0, MAX_COMPLETION_FINDINGS).map((f) => {\n const source = f.current ?? f.original;\n if (source.evidence.kind === \"decision-anchor\") {\n const evidence = source.evidence;\n return [\n `[advisory] Decision \"${safe(evidence.title)}\" has ${evidence.changedFiles.length} anchored file(s) changed since capture or review; consistency is unchecked.`,\n `Inspect the decision and diff with review_decision(action: \"prepare\", id: ${JSON.stringify(evidence.decisionId)}). Record action: \"inspect\" after source inspection finds no contradiction; human acceptance is not required.`,\n `Changed files: ${safe(evidence.changedFiles.join(\", \"))}`,\n ].join(\"\\n\");\n }\n return `[${f.status === \"unresolved\" ? \"issue\" : \"advisory\"}] ${safe(source.anchor.doc)}: ${safe(source.message)}`;\n }),\n ...(findings.length > MAX_COMPLETION_FINDINGS\n ? [`${findings.length - MAX_COMPLETION_FINDINGS} more in the report; pending later feedback.`]\n : []),\n `Evidence: ${reportPath}. Explicit audit: mason_automation(action: \"check\").`,\n \"Inspect findings within the authorized work. Concurrent changes may also contribute; this summary does not establish who introduced them.\",\n ].join(\"\\n\");\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport fs from \"node:fs/promises\";\nimport { z } from \"zod\";\nimport { readStoreJson } from \"../utils/storage.js\";\nimport { pathChangeSchema } from \"./path.js\";\n\nexport const releaseVersion = z.string().regex(/^\\d+\\.\\d+\\.\\d+(?:-[A-Za-z0-9.-]+)?$/);\nconst bundleId = z.string().regex(/^[a-f0-9]{24}$/);\nexport const updatePolicySchema = z.object({\n enabled: z.boolean(),\n revision: z.string(),\n pinnedVersion: releaseVersion.optional(),\n mirror: z.string().optional(),\n});\nexport const recordSchema = z.object({\n format: z.literal(1),\n home: z.string(),\n bin: z.string(),\n current: bundleId,\n version: releaseVersion,\n launchers: z.record(z.string()),\n previousLaunchers: z.record(z.string()).optional(),\n pathChanges: z.array(pathChangeSchema).optional(),\n updates: updatePolicySchema.optional(),\n previous: z.object({ id: bundleId, version: releaseVersion }).optional(),\n pending: z\n .object({ id: bundleId, version: releaseVersion, policyRevision: z.string() })\n .optional(),\n});\nexport type InstallRecord = z.infer<typeof recordSchema>;\nexport const defaultHome = () =>\n process.env.MASON_HOME ??\n (process.platform === \"win32\"\n ? path.join(process.env.LOCALAPPDATA ?? os.homedir(), \"Mason\")\n : path.join(os.homedir(), \".local/share/mason\"));\n\nexport async function readInstallation(home: string): Promise<InstallRecord> {\n const raw = await readStoreJson(home, \"install.json\");\n if (!raw)\n throw new Error(\n \"Run this command from a standalone installation. npm users can upgrade with npm.\",\n );\n const record = recordSchema.parse(raw);\n if (record.home !== (await fs.realpath(home)))\n throw new Error(\"Invalid standalone installation receipt.\");\n return record;\n}\n\nexport function standaloneLocation() {\n // Distribution modules are bundled into app/dist/mason.js (or mason-mcp.js).\n const bundle = path.resolve(path.dirname(fileURLToPath(import.meta.url)), \"../..\");\n const home = path.dirname(path.dirname(bundle));\n if (\n path.dirname(bundle) !== path.join(home, \"versions\") ||\n !/^[a-f0-9]{24}$/.test(path.basename(bundle))\n ) {\n return null;\n }\n return { home, bundle };\n}\n\nexport async function currentInstallation() {\n const location = standaloneLocation();\n if (!location)\n throw new Error(\n \"Run this command from a standalone installation. npm users can upgrade with npm.\",\n );\n const { home, bundle } = location;\n const record = await readInstallation(home);\n return { home, bundle, record };\n}\n\nexport function automaticUpdatesAllowed(\n record: InstallRecord,\n env: NodeJS.ProcessEnv = process.env,\n): boolean {\n return (\n record.updates?.enabled === true &&\n !record.updates.pinnedVersion &&\n !record.updates.mirror &&\n !env.CI &&\n env.MASON_NO_AUTO_UPDATE !== \"1\" &&\n !env.MASON_RELEASE_BASE &&\n !env.MASON_VERSION\n );\n}\n\n/** Automatic updates accept stable releases only and never downgrade. */\nexport function newerStableVersion(candidate: string, installed: string): boolean {\n if (!/^\\d+\\.\\d+\\.\\d+$/.test(candidate)) return false;\n const a = candidate.split(\".\").map(BigInt),\n b = installed.split(\"-\")[0].split(\".\").map(BigInt);\n for (let i = 0; i < 3; i++) {\n if (a[i] !== b[i]) return a[i] > b[i];\n }\n return installed.includes(\"-\");\n}\n","import { hasErrorCode } from \"../utils/validation.js\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { z } from \"zod\";\n\nconst execute = promisify(execFile);\nexport const pathChangeSchema = z.discriminatedUnion(\"kind\", [\n z.object({\n kind: z.literal(\"profile\"),\n file: z.string(),\n block: z.string(),\n created: z.boolean(),\n }),\n z.object({ kind: z.literal(\"windows\"), entry: z.string(), created: z.boolean() }),\n]);\nexport type PathChange = z.infer<typeof pathChangeSchema>;\nexport type PathResult = {\n status: \"configured\" | \"manual\";\n message: string;\n changes: PathChange[];\n};\nexport type PathOptions = {\n platform?: NodeJS.Platform;\n userHome?: string;\n env?: NodeJS.ProcessEnv;\n};\nconst quote = (s: string) => \"'\" + s.replace(/'/g, \"'\\\\''\") + \"'\";\n\n// Store the ownership intent before changing settings, so interrupted installs\n// can resume and uninstall can distinguish our addition from existing settings.\nexport async function configurePath(\n bin: string,\n previous: PathChange[],\n save: (changes: PathChange[]) => Promise<void>,\n options: PathOptions = {},\n): Promise<PathResult> {\n const env = options.env ?? process.env,\n platform = options.platform ?? process.platform;\n const changes = [...previous];\n // Profile/registry changes affect future shells. The inherited PATH tells us\n // whether this terminal already has the installation directory available.\n const currentPath =\n platform === \"win32\"\n ? Object.entries(env).find(([key]) => key.toLowerCase() === \"path\")?.[1]\n : env.PATH;\n const alreadyOnPath =\n platform === \"win32\"\n ? hasWindowsEntry(currentPath ?? null, bin, env)\n : (currentPath ?? \"\")\n .split(\":\")\n .some(\n (entry) =>\n path.posix.isAbsolute(entry) && path.posix.resolve(entry) === path.posix.resolve(bin),\n );\n const setupHint =\n \"To prepare a new project for review, run one of these inside its directory (use mason setup to activate existing team configuration):\\n Codex: mason init --host codex\\n Claude Code: mason init --host claude\";\n const message = (alreadyOnPath ? \"\" : \"Open a new terminal to use mason.\\n\") + setupHint;\n const manual = (reason: string): PathResult => ({\n status: \"manual\",\n changes,\n message: `${reason}\\n${alreadyOnPath ? \"\" : `Add ${bin} to your PATH.\\n`}${setupHint}`,\n });\n if (env.MASON_NO_MODIFY_PATH === \"1\")\n return manual(\"Automatic PATH setup is disabled (MASON_NO_MODIFY_PATH=1).\");\n if (/[\\r\\n\\0]/.test(bin) || bin.includes(platform === \"win32\" ? \";\" : \":\"))\n return manual(\"This installation path cannot be represented safely in PATH.\");\n try {\n if (platform === \"win32\") {\n const current = await windowsPath(\"read\");\n if (!hasWindowsEntry(current.value, bin)) {\n if (!changes.some((c) => c.kind === \"windows\" && c.entry === bin)) {\n changes.push({ kind: \"windows\", entry: bin, created: current.value === null });\n await save(changes);\n }\n await windowsPath(\"add\", bin, current);\n }\n return { status: \"configured\", changes, message };\n }\n const shell = path.basename(env.SHELL ?? os.userInfo().shell ?? \"\");\n const userHome = options.userHome ?? os.homedir();\n let profiles: string[];\n if (![\"bash\", \"zsh\", \"sh\"].includes(shell))\n return manual(`Automatic PATH setup is unavailable for ${shell || \"an unknown shell\"}.`);\n if (env.MASON_PROFILE) profiles = [path.resolve(env.MASON_PROFILE)];\n else if (shell === \"zsh\") profiles = [path.join(env.ZDOTDIR || userHome, \".zshrc\")];\n else if (shell === \"sh\") profiles = [path.join(userHome, \".profile\")];\n else {\n const candidates = [\".bash_profile\", \".bash_login\", \".profile\"].map((name) =>\n path.join(userHome, name),\n );\n let login = candidates[2];\n for (const file of candidates)\n if (\n await fs.lstat(file).then(\n () => true,\n (error: unknown) => {\n if (hasErrorCode(error, \"ENOENT\")) return false;\n throw error;\n },\n )\n ) {\n login = file;\n break;\n }\n profiles = [path.join(userHome, \".bashrc\"), login];\n }\n const marker = `# >>> mason PATH ${createHash(\"sha256\").update(bin).digest(\"hex\").slice(0, 12)} >>>`;\n for (const profile of profiles) {\n // Preserve symlinked dotfiles by updating their target. A dangling link is\n // an error, not permission to replace the link with an unrelated file.\n const stat = await fs.lstat(profile).catch((error: unknown) => {\n if (hasErrorCode(error, \"ENOENT\")) return null;\n throw error;\n });\n const file = stat?.isSymbolicLink() ? await fs.realpath(profile) : path.resolve(profile);\n const original = await fs.readFile(file).catch((error: unknown) => {\n if (hasErrorCode(error, \"ENOENT\") && !stat) return null;\n throw error;\n });\n if (original && !Buffer.from(original.toString(\"utf8\")).equals(original))\n return manual(`Shell profile is not UTF-8: ${profile}.`);\n const text = original?.toString(\"utf8\") ?? \"\";\n const owned = changes.find(\n (c): c is Extract<PathChange, { kind: \"profile\" }> =>\n c.kind === \"profile\" && c.file === file,\n );\n if (owned && text.includes(owned.block)) continue;\n if (text.includes(marker) || (owned && text.includes(\"# <<< mason PATH\")))\n return manual(`Mason's PATH block was edited; retained ${profile}.`);\n const newline = text.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n const block =\n owned?.block ??\n (\n \"\\n\" +\n marker +\n '\\ncase \":${PATH-}:\" in\\n *:' +\n quote(bin) +\n \":*) ;;\\n *) export PATH=\" +\n quote(bin) +\n ':\"${PATH-}\" ;;\\nesac\\n# <<< mason PATH <<<\\n'\n ).replaceAll(\"\\n\", newline);\n if (!owned) {\n changes.push({ kind: \"profile\", file, block, created: original === null });\n await save(changes);\n }\n await fs.mkdir(path.dirname(file), { recursive: true });\n // Append without rewriting existing bytes, modes or symlinks. Recheck the\n // bytes on the opened file to detect edits since the preflight read.\n const handle = await fs.open(file, original === null ? \"ax+\" : \"a+\", 0o600);\n try {\n if (!(await handle.readFile()).equals(original ?? Buffer.alloc(0)))\n throw new Error(`Shell profile changed during installation: ${profile}`);\n await handle.writeFile(block);\n await handle.sync();\n } finally {\n await handle.close();\n }\n }\n return { status: \"configured\", changes, message };\n } catch (error) {\n return manual(\n `Could not configure PATH: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\nexport async function removePath(changes: PathChange[]): Promise<string[]> {\n const warnings: string[] = [];\n for (const change of changes) {\n try {\n if (change.kind === \"windows\") {\n const current = await windowsPath(\"read\");\n if (\n !(current.value ?? \"\").split(\";\").includes(change.entry) &&\n hasWindowsEntry(current.value, change.entry)\n )\n throw new Error(\"PATH entry was edited\");\n await windowsPath(\"remove\", change.entry, current, change.created);\n continue;\n }\n const stat = await fs.lstat(change.file).catch((error: unknown) => {\n if (hasErrorCode(error, \"ENOENT\")) return null;\n throw error;\n });\n if (!stat) continue;\n if (!stat.isFile()) throw new Error(\"profile type changed\");\n const bytes = await fs.readFile(change.file),\n text = bytes.toString(\"utf8\");\n if (!Buffer.from(text).equals(bytes)) throw new Error(\"profile encoding changed\");\n const index = text.indexOf(change.block);\n if (index < 0 || text.indexOf(change.block, index + change.block.length) >= 0)\n throw new Error(\"PATH block was edited or removed\");\n const prefix = text.slice(0, index),\n suffix = text.slice(index + change.block.length);\n const separator =\n prefix && suffix && !prefix.endsWith(\"\\n\")\n ? change.block.startsWith(\"\\r\\n\")\n ? \"\\r\\n\"\n : \"\\n\"\n : \"\";\n const next = Buffer.from(prefix + separator + suffix);\n const temporary = change.file + \".mason-\" + randomUUID();\n try {\n if (stat.nlink > 1) throw new Error(\"profile has multiple hard links\");\n const handle = await fs.open(temporary, \"wx\", stat.mode & 0o777);\n try {\n await handle.writeFile(next);\n await handle.chmod(stat.mode & 0o777);\n if (process.platform !== \"win32\") await handle.chown(stat.uid, stat.gid);\n await handle.sync();\n } finally {\n await handle.close();\n }\n const current = await fs.lstat(change.file);\n if (\n !current.isFile() ||\n current.ino !== stat.ino ||\n !(await fs.readFile(change.file)).equals(bytes)\n )\n throw new Error(\"profile changed during uninstall\");\n await fs.rename(temporary, change.file);\n } finally {\n await fs.rm(temporary, { force: true });\n }\n // Empty created profiles are harmless; retain them to avoid masking any\n // dotfile another process created or linked since installation.\n } catch (error) {\n warnings.push(\n `Retained PATH setting ${change.kind === \"profile\" ? change.file : change.entry}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n return warnings;\n}\n\nexport type WindowsPath = { value: string | null; kind: string | null };\nconst normalizeWindowsEntry = (value: string) =>\n path.win32\n .normalize(value.trim().replace(/^\"(.*)\"$/, \"$1\"))\n .replace(/[\\\\/]+$/, \"\")\n .toLowerCase();\nexport const hasWindowsEntry = (\n value: string | null,\n entry: string,\n env: NodeJS.ProcessEnv = process.env,\n) => {\n const variables = new Map(Object.entries(env).map(([key, value]) => [key.toLowerCase(), value]));\n const expand = (part: string) =>\n part.replace(/%([^%]+)%/g, (match, key: string) => variables.get(key.toLowerCase()) ?? match);\n return (value ?? \"\")\n .split(\";\")\n .some((part) => normalizeWindowsEntry(expand(part)) === normalizeWindowsEntry(entry));\n};\n\n// Read unexpanded registry text and preserve its value kind. No machine PATH,\n// setx truncation, or expanded copy of the process environment is written.\nexport async function windowsPath(\n action: \"read\" | \"add\" | \"remove\",\n entry = \"\",\n expected?: WindowsPath,\n removeEmpty = false,\n): Promise<WindowsPath> {\n const payload = Buffer.from(JSON.stringify({ action, entry, expected, removeEmpty })).toString(\n \"base64\",\n );\n const script = `$ErrorActionPreference = 'Stop'\n[Console]::OutputEncoding = New-Object Text.UTF8Encoding($false)\n$p = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${payload}')) | ConvertFrom-Json\n$key = [Microsoft.Win32.Registry]::CurrentUser.CreateSubKey('Environment')\ntry {\n $value = $key.GetValue('Path', $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)\n $kind = if ($null -eq $value) { $null } else { $key.GetValueKind('Path').ToString() }\n if ($null -ne $kind -and $kind -notin @('String','ExpandString')) { throw 'User PATH is not a string.' }\n if ($p.action -eq 'read') { @{ value = $value; kind = $kind } | ConvertTo-Json -Compress; exit 0 }\n if ($p.expected -and ($value -cne $p.expected.value -or $kind -cne $p.expected.kind)) { throw 'User PATH changed concurrently; retry the operation.' }\n $parts = if ($null -eq $value -or $value -eq '') { @() } else { @($value.Split(';')) }\n if ($p.action -eq 'add') { $next = (@($p.entry) + $parts) -join ';' }\n else { $next = (@($parts | Where-Object { $_ -cne $p.entry })) -join ';' }\n if ($next -cne $value) {\n $writeKind = if ($kind) { [Microsoft.Win32.RegistryValueKind]$kind } else { [Microsoft.Win32.RegistryValueKind]::ExpandString }\n if ($p.action -eq 'remove' -and $p.removeEmpty -and $next -eq '') { $key.DeleteValue('Path', $false) }\n else { $key.SetValue('Path', $next, $writeKind) }\n Add-Type -TypeDefinition 'using System; using System.Runtime.InteropServices; public class MasonEnvironment { [DllImport(\"user32.dll\", CharSet=CharSet.Unicode, SetLastError=true)] public static extern IntPtr SendMessageTimeout(IntPtr h, uint m, UIntPtr w, string l, uint f, uint t, out UIntPtr r); }'\n $result = [UIntPtr]::Zero\n [void][MasonEnvironment]::SendMessageTimeout([IntPtr]0xffff, 0x1a, [UIntPtr]::Zero, 'Environment', 2, 1000, [ref]$result)\n }\n @{ value = $next; kind = $kind } | ConvertTo-Json -Compress\n} finally { $key.Dispose() }\n`;\n const { stdout } = await execute(\n \"powershell.exe\",\n [\n \"-NoProfile\",\n \"-NonInteractive\",\n \"-ExecutionPolicy\",\n \"Bypass\",\n \"-EncodedCommand\",\n Buffer.from(script, \"utf16le\").toString(\"base64\"),\n ],\n { windowsHide: true, timeout: 15000, maxBuffer: 1024 * 1024 },\n );\n return z\n .object({ value: z.string().nullable(), kind: z.string().nullable() })\n .parse(JSON.parse(stdout.replace(/^\\uFEFF/, \"\").trim()));\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\nimport { storePath } from \"../utils/storage.js\";\n\nexport const targets = [\n \"darwin-arm64\",\n \"darwin-x64\",\n \"linux-arm64\",\n \"linux-x64\",\n \"win32-arm64\",\n \"win32-x64\",\n] as const;\nconst digest = z.string().regex(/^[a-f0-9]{64}$/);\nexport const bundleSchema = z\n .object({\n format: z.literal(1),\n version: z.string().regex(/^\\d+\\.\\d+\\.\\d+(?:-[A-Za-z0-9.-]+)?$/),\n target: z.enum(targets),\n nodeVersion: z.string(),\n files: z.record(digest),\n })\n .strict();\nexport const sha256 = (bytes: Buffer | string) => createHash(\"sha256\").update(bytes).digest(\"hex\");\n\nexport async function readBundle(root: string) {\n const bytes = await fs.readFile(await storePath(root, \"bundle.json\"));\n const manifest = bundleSchema.parse(JSON.parse(bytes.toString(\"utf8\")));\n const windows = manifest.target.startsWith(\"win32-\");\n for (const file of Object.keys(manifest.files)) {\n if (\n file.includes(\"\\\\\") ||\n file.split(\"/\").some((p) => !p || p === \".\" || p === \"..\") ||\n path.isAbsolute(file)\n ) {\n throw new Error(\"Unsafe path in Mason bundle: \" + file);\n }\n }\n for (const required of [\n windows ? \"node.exe\" : \"node\",\n \"app/package.json\",\n \"app/dist/mason.js\",\n \"app/dist/mason-mcp.js\",\n ]) {\n if (!manifest.files[required]) throw new Error(\"Incomplete Mason bundle: \" + required);\n }\n return { manifest, manifestHash: sha256(bytes) };\n}\n\n/** Verify every shipped file before copying or selecting a standalone distribution. */\nexport async function verifyBundle(root: string, expectedHash?: string) {\n const bundle = await readBundle(root);\n if (expectedHash && bundle.manifestHash !== expectedHash)\n throw new Error(\"Mason bundle manifest changed.\");\n if (bundle.manifest.target !== `${process.platform}-${process.arch}`)\n throw new Error(\n \"Mason bundle is for another platform. Install this platform's bundle and rerun setup.\",\n );\n const files = Object.keys(bundle.manifest.files);\n // Bound concurrent reads; Node itself is a large file.\n for (let i = 0; i < files.length; i += 8)\n await Promise.all(\n files.slice(i, i + 8).map(async (file) => {\n if (sha256(await fs.readFile(await storePath(root, file))) !== bundle.manifest.files[file])\n throw new Error(\"Mason bundle checksum mismatch: \" + file);\n }),\n );\n return bundle;\n}\n","import tls from \"node:tls\";\n\n/** Called by standalone startup before any application network requests. */\nexport function configureSystemTrust() {\n if (process.env.NODE_USE_SYSTEM_CA === \"0\") return;\n // Older runtimes may lack these APIs; the bundled Node supports both.\n if (\n typeof tls.getCACertificates !== \"function\" ||\n typeof tls.setDefaultCACertificates !== \"function\"\n )\n return;\n tls.setDefaultCACertificates([\n ...tls.getCACertificates(\"default\"),\n ...tls.getCACertificates(\"system\"),\n ]);\n}\n\nexport function releaseRequestError(error: unknown): Error {\n const cause = error instanceof Error ? error.cause : undefined;\n const code =\n cause && typeof cause === \"object\" && \"code\" in cause ? String(cause.code) : undefined;\n const detail = code ?? (error instanceof Error ? error.message : String(error));\n const certificateError = code && /CERT|SELF_SIGNED|UNABLE_TO_VERIFY_LEAF_SIGNATURE/.test(code);\n const hint = certificateError\n ? \" Check that your company's CA certificate is trusted by your operating system, or provide its PEM file with NODE_EXTRA_CA_CERTS.\"\n : \" Check network and proxy access to GitHub.\";\n return new Error(`Could not check the latest Mason release on GitHub (${detail}).${hint}`, {\n cause: error,\n });\n}\n"],"mappings":"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","names":["fs","os","path","randomUUID","z","z","fs","fs","path","path","fs","path","fs","z","fs","constants","z","fg","fg","AsyncLocalStorage","path","AsyncLocalStorage","z","createHash","z","z","evidenceSchema","z","z","z","z","z","z","os","path","fs","z","fs","os","path","execFile","promisify","createHash","randomUUID","z","execute","z","fs","path","fs","path","createHash","z","digest","z","z","randomUUID","fs","os","path"]}