@glasstrace/sdk 1.1.1 → 1.1.3

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.
Files changed (49) hide show
  1. package/README.md +78 -1
  2. package/dist/{chunk-DIM4JRXM.js → chunk-2M57EO6U.js} +2 -2
  3. package/dist/{chunk-Y26HJUPD.js → chunk-FGDS33I2.js} +138 -12
  4. package/dist/chunk-FGDS33I2.js.map +1 -0
  5. package/dist/{chunk-MXDZHFJQ.js → chunk-JKI4OCFV.js} +4 -14
  6. package/dist/chunk-JKI4OCFV.js.map +1 -0
  7. package/dist/{chunk-7SZQN6IU.js → chunk-NB7GJE4S.js} +2 -2
  8. package/dist/chunk-NB7GJE4S.js.map +1 -0
  9. package/dist/{chunk-ZRDQ6ZKI.js → chunk-TWHCJKRS.js} +101 -481
  10. package/dist/chunk-TWHCJKRS.js.map +1 -0
  11. package/dist/{chunk-P22UQ2OJ.js → chunk-TWTWRJ25.js} +233 -9
  12. package/dist/chunk-TWTWRJ25.js.map +1 -0
  13. package/dist/cli/init.cjs +2494 -2332
  14. package/dist/cli/init.cjs.map +1 -1
  15. package/dist/cli/init.js +434 -63
  16. package/dist/cli/init.js.map +1 -1
  17. package/dist/cli/mcp-add.cjs +14 -2
  18. package/dist/cli/mcp-add.cjs.map +1 -1
  19. package/dist/cli/mcp-add.js +17 -5
  20. package/dist/cli/mcp-add.js.map +1 -1
  21. package/dist/cli/status.cjs.map +1 -1
  22. package/dist/cli/status.js +1 -3
  23. package/dist/cli/status.js.map +1 -1
  24. package/dist/cli/uninit.cjs +116 -14
  25. package/dist/cli/uninit.cjs.map +1 -1
  26. package/dist/cli/uninit.js +3 -3
  27. package/dist/cli/validate.cjs +14162 -2
  28. package/dist/cli/validate.cjs.map +1 -1
  29. package/dist/cli/validate.d.cts +7 -3
  30. package/dist/cli/validate.d.ts +7 -3
  31. package/dist/cli/validate.js +25 -2
  32. package/dist/cli/validate.js.map +1 -1
  33. package/dist/index.cjs +339 -28
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +12 -9
  36. package/dist/index.d.ts +12 -9
  37. package/dist/index.js +3 -3
  38. package/dist/{monorepo-GSL6JD3G.js → monorepo-PFVNPQ6X.js} +3 -5
  39. package/dist/node-entry.cjs +339 -28
  40. package/dist/node-entry.cjs.map +1 -1
  41. package/dist/node-entry.js +3 -3
  42. package/package.json +1 -1
  43. package/dist/chunk-7SZQN6IU.js.map +0 -1
  44. package/dist/chunk-MXDZHFJQ.js.map +0 -1
  45. package/dist/chunk-P22UQ2OJ.js.map +0 -1
  46. package/dist/chunk-Y26HJUPD.js.map +0 -1
  47. package/dist/chunk-ZRDQ6ZKI.js.map +0 -1
  48. /package/dist/{chunk-DIM4JRXM.js.map → chunk-2M57EO6U.js.map} +0 -0
  49. /package/dist/{monorepo-GSL6JD3G.js.map → monorepo-PFVNPQ6X.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/init.ts"],"sourcesContent":["#!/usr/bin/env node\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as readline from \"node:readline\";\nimport {\n scaffoldInstrumentation,\n scaffoldNextConfig,\n scaffoldEnvLocal,\n scaffoldGitignore,\n addCoverageMapEnv,\n mcpConfigMatches,\n readEnvLocalApiKey,\n isDevApiKey,\n resolveInstrumentationTarget,\n} from \"./scaffolder.js\";\nimport {\n identityFingerprint,\n resolveEffectiveMcpCredential,\n writeMcpMarker,\n} from \"../mcp-runtime.js\";\nimport { buildImportGraph } from \"../import-graph.js\";\nimport { getOrCreateAnonKey, readAnonKey } from \"../anon-key.js\";\nimport { detectAgents } from \"../agent-detection/detect.js\";\nimport { generateMcpConfig, generateInfoSection } from \"../agent-detection/configs.js\";\nimport { writeMcpConfig, injectInfoSection, updateGitignore } from \"../agent-detection/inject.js\";\nimport type { DetectedAgent } from \"../agent-detection/detect.js\";\nimport { MCP_ENDPOINT, NEXT_CONFIG_NAMES, formatAgentName } from \"./constants.js\";\nimport { resolveProjectRoot } from \"./monorepo.js\";\nimport {\n isInitCreatedInstrumentation,\n removeRegisterGlasstrace,\n unwrapExport,\n unwrapCJSExport,\n removeGlasstraceConfigImport,\n} from \"./uninit.js\";\nimport { verifyInitReachable, type VerifyInitResult } from \"../init-client.js\";\nimport { resolveConfig } from \"../env-detection.js\";\nimport {\n writeDiscoveryFile,\n removeDiscoveryFile,\n relativeDiscoveryPath,\n resolveStaticRoot,\n} from \"./discovery-file.js\";\n\n// Declare the tsup-injected SDK version literal. Replaced at build time\n// via `define` in tsup.config.ts. Falls back to \"0.0.0-dev\" when\n// running tests under vitest (no tsup build step).\ndeclare const __SDK_VERSION__: string;\n\n/**\n * Returns true if the current Node.js major version meets the minimum requirement.\n * Exported for testability — the CLI entry point uses this to gate execution.\n */\nexport function meetsNodeVersion(minMajor: number): boolean {\n const [major] = process.versions.node.split(\".\").map(Number);\n return major >= minMajor;\n}\n\n/** Options for the init command (parsed from CLI args or passed programmatically). */\nexport interface InitOptions {\n projectRoot: string;\n yes: boolean;\n coverageMap: boolean;\n /**\n * When true, skip interactive confirmation and overwrite existing\n * MCP configuration files without prompting. Preservation of the\n * anonymous key, config cache, and developer API key still applies\n * regardless of this flag — `--force` only affects the MCP diff\n * prompt (DISC-1247 Scenario 2c). Defaults to `false`.\n */\n force?: boolean;\n}\n\n/** Result of running the init command. */\nexport interface InitResult {\n exitCode: number;\n summary: string[];\n warnings: string[];\n errors: string[];\n}\n\n/**\n * Decides whether the MCP config at `configPath` should be overwritten\n * during re-init. Returns the action to take.\n *\n * - `\"write\"` — file does not exist, or existing content already matches\n * the expected content. Safe to write.\n * - `\"skip\"` — existing file differs AND the user chose to keep it, or\n * we are in a non-interactive environment without `--force`.\n * - `\"force-overwrite\"` — `force === true` (or user accepted the prompt)\n * and content differs; overwrite.\n *\n * The prompt is skipped entirely when `force` is true (non-interactive\n * overwrite) or when there is no existing file / content already matches.\n *\n * @internal Exported for unit testing only.\n */\nexport async function decideMcpConfigAction(options: {\n configPath: string | null;\n expectedContent: string;\n force: boolean;\n readFile?: (p: string) => string;\n existsSync?: (p: string) => boolean;\n prompt?: (question: string, defaultValue: boolean) => Promise<boolean>;\n}): Promise<\"write\" | \"skip\" | \"force-overwrite\"> {\n const { configPath, expectedContent, force } = options;\n if (configPath === null) return \"write\";\n\n const exists = options.existsSync ?? fs.existsSync;\n const read = options.readFile ?? ((p: string) => fs.readFileSync(p, \"utf-8\"));\n const prompt = options.prompt ?? promptYesNo;\n\n if (!exists(configPath)) return \"write\";\n\n let existingContent: string;\n try {\n existingContent = read(configPath);\n } catch {\n // Unreadable — treat as \"write\" since we can't assess drift.\n // This preserves the pre-hardening behavior for corrupt or\n // permission-restricted files.\n return \"write\";\n }\n\n if (mcpConfigMatches(existingContent, expectedContent)) {\n return \"write\";\n }\n\n if (force) {\n return \"force-overwrite\";\n }\n\n const answer = await prompt(\n `Existing MCP config at ${configPath} differs from Glasstrace's template. Overwrite?`,\n false,\n );\n return answer ? \"force-overwrite\" : \"skip\";\n}\n\n/**\n * Prompts the user with a yes/no question. Returns true for yes.\n * In non-interactive mode (no TTY), returns the default value.\n */\nasync function promptYesNo(question: string, defaultValue: boolean): Promise<boolean> {\n if (!process.stdin.isTTY) {\n return defaultValue;\n }\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise<boolean>((resolve) => {\n const suffix = defaultValue ? \" [Y/n] \" : \" [y/N] \";\n rl.question(question + suffix, (answer) => {\n rl.close();\n const trimmed = answer.trim().toLowerCase();\n if (trimmed === \"\") {\n resolve(defaultValue);\n return;\n }\n resolve(trimmed === \"y\" || trimmed === \"yes\");\n });\n });\n}\n\n/**\n * Identifies a scaffolding step that can be reversed during rollback.\n * Steps are tracked in execution order and rolled back in reverse.\n */\ntype CompletedStep =\n | \"instrumentation\"\n | \"next-config\"\n | \"env-local\"\n | \"gitignore\"\n | \"discovery-file\";\n\n/**\n * Tracks state needed for accurate rollback of init steps.\n * Separating this from the step list allows rollback to restore\n * original file content rather than doing surgical removal.\n */\ninterface RollbackState {\n steps: CompletedStep[];\n /**\n * Absolute path of the instrumentation file that the scaffolder\n * wrote to. May be either `{root}/instrumentation.ts` or\n * `{root}/src/instrumentation.ts` depending on the project layout\n * (DISC-493 Issue 1). When absent, rollback falls back to the\n * root path for backward compatibility with callers that do not\n * populate this field.\n */\n instrumentationPath?: string;\n /** Original instrumentation.ts content saved before injection.\n * When present, rollback restores this instead of using removeRegisterGlasstrace. */\n originalInstrumentationContent?: string;\n}\n\n/**\n * Removes leading blank lines that can appear after removing import lines.\n * Duplicated from uninit.ts to avoid exporting a trivial utility.\n */\nfunction cleanLeadingBlankLines(content: string): string {\n return content.replace(/^\\n{2,}/, \"\\n\");\n}\n\n/**\n * Returns true when the given `.gitignore` content would exclude the\n * static discovery file at `<root>/public/.well-known/glasstrace.json`\n * or `<root>/static/.well-known/glasstrace.json` (depending on `layout`)\n * from being committed.\n *\n * Model: the file has three ancestors in its committed path — the\n * static root directory (`public/` or `static/`), the `.well-known/`\n * sub-directory, and the file itself. Each pattern in `.gitignore` is\n * classified by which ancestor paths it matches, and each ancestor\n * carries its own current ignore state that later patterns can flip.\n * Patterns that don't match any of the three ancestors are ignored.\n *\n * Per `gitignore(5)`:\n *\n * > It is not possible to re-include a file if a parent directory of\n * > that file is excluded.\n *\n * Consequently, the file is reported as ignored when the final state\n * of ANY ancestor (static root, `.well-known/`, or the file itself)\n * is ignored — a `!<file>` negation alone cannot \"escape\" an ignored\n * ancestor.\n *\n * Per-ancestor tracking is what distinguishes overlapping patterns:\n *\n * - `public/` then `!public/` — root re-included, file OK.\n * - `public/` then `!public/.well-known/` — root still ignored (scope-2\n * negation doesn't match the scope-1 ancestor path), file ignored.\n * - `.well-known/` then `!public/.well-known/` — `.well-known/` re-included\n * (both patterns match the scope-2 ancestor), file OK.\n * - `.well-known/` then `!public/` — `!public/` matches the\n * scope-1 ancestor only; the scope-2 ancestor (`.well-known/`) is still\n * ignored, so the file is ignored.\n * - `<file>` then `!<file>` — file-level ignore flipped.\n * - `<file>` then `!public/.well-known/` — directory negation does\n * not match the file path; file still ignored.\n *\n * Not-modeled rules — glob wildcards (star, question mark, character\n * classes), overlapping any-depth matches across multiple parents, and\n * nested `.gitignore` files — skew the heuristic toward false positives\n * (extra warning output) rather than false negatives. The warning is\n * advisory.\n *\n * @internal Exported for unit testing only.\n */\nexport function gitignoreExcludesDiscoveryFile(\n gitignoreContent: string,\n layout: \"public\" | \"static\",\n): boolean {\n const staticRoot = layout === \"static\" ? \"static\" : \"public\";\n\n // Per-ancestor target sets. `matchesDiscoveryPath` handles trailing\n // slash and leading `**/` shapes on top of these exact paths.\n const rootTargets = [staticRoot, `${staticRoot}/`];\n const wellKnownTargets = [\n `${staticRoot}/.well-known`,\n `${staticRoot}/.well-known/`,\n \".well-known\",\n \".well-known/\",\n ];\n const fileTargets = [\n `${staticRoot}/.well-known/glasstrace.json`,\n \".well-known/glasstrace.json\",\n ];\n\n // Current ignore state of each ancestor path. Later patterns flip\n // these independently because a pattern only affects ancestors it\n // actually matches.\n let rootIgnored = false;\n let wellKnownIgnored = false;\n let fileIgnored = false;\n\n for (const rawLine of gitignoreContent.split(\"\\n\")) {\n const line = rawLine.trim();\n if (line === \"\" || line.startsWith(\"#\")) continue;\n\n const negation = line.startsWith(\"!\");\n const pattern = negation ? line.slice(1).trim() : line;\n if (pattern === \"\") continue;\n\n // Normalize: strip a leading slash (anchors to root in git's grammar;\n // for our purposes the patterns we check are all root-relative).\n const normalized = pattern.startsWith(\"/\") ? pattern.slice(1) : pattern;\n\n const matchesRoot = matchesDiscoveryPath(normalized, rootTargets);\n const matchesWellKnown = matchesDiscoveryPath(normalized, wellKnownTargets);\n const matchesFile = matchesDiscoveryPath(normalized, fileTargets);\n\n if (!matchesRoot && !matchesWellKnown && !matchesFile) continue;\n\n const newState = !negation;\n if (matchesRoot) rootIgnored = newState;\n if (matchesWellKnown) wellKnownIgnored = newState;\n if (matchesFile) fileIgnored = newState;\n }\n\n // A file is ignored whenever any ancestor's final state is ignored;\n // gitignore(5) does not permit re-including a file below an ignored\n // parent with a file-level `!<path>` pattern alone.\n return rootIgnored || wellKnownIgnored || fileIgnored;\n}\n\n/**\n * Returns true when the (normalized) gitignore pattern matches any of the\n * discovery-file-relevant paths. Supports three common pattern shapes:\n *\n * - Exact path: e.g. `public/.well-known/glasstrace.json`\n * - Directory: e.g. `.well-known/` or `public/.well-known`\n * - Leading `**\\/`: e.g. `**\\/.well-known/` (any-depth wildcard)\n *\n * Complex glob patterns (`*`, `?`, character classes) are not modeled;\n * the warning is advisory and false negatives are acceptable.\n */\nfunction matchesDiscoveryPath(\n pattern: string,\n targets: string[],\n): boolean {\n // Strip trailing slash — both `.well-known` and `.well-known/` should\n // match a directory-style target.\n const bare = pattern.endsWith(\"/\") ? pattern.slice(0, -1) : pattern;\n\n for (const target of targets) {\n const tBare = target.endsWith(\"/\") ? target.slice(0, -1) : target;\n if (bare === tBare) return true;\n // `**/X` matches any-depth occurrence of X\n if (pattern.startsWith(\"**/\")) {\n const suffix = pattern.slice(3);\n const sBare = suffix.endsWith(\"/\") ? suffix.slice(0, -1) : suffix;\n if (tBare === sBare || tBare.endsWith(`/${sBare}`)) return true;\n }\n }\n return false;\n}\n\n/**\n * Best-effort rollback of completed init steps in reverse order.\n * Each step is individually try/caught so that a failure in one\n * rollback does not prevent the remaining steps from being attempted.\n *\n * @internal Exported for unit testing only.\n */\nexport async function rollbackSteps(\n steps: CompletedStep[],\n projectRoot: string,\n state?: Omit<RollbackState, \"steps\">,\n): Promise<void> {\n for (const step of [...steps].reverse()) {\n try {\n switch (step) {\n case \"instrumentation\": {\n // Prefer the exact path the scaffolder wrote to — the resolver\n // may have chosen `src/instrumentation.ts` on Next.js `src/`\n // layouts (DISC-493 Issue 1). Fall back to the root path for\n // callers that do not populate `instrumentationPath`.\n const instrPath =\n state?.instrumentationPath ?? path.join(projectRoot, \"instrumentation.ts\");\n if (fs.existsSync(instrPath)) {\n const content = fs.readFileSync(instrPath, \"utf-8\");\n if (isInitCreatedInstrumentation(content)) {\n fs.unlinkSync(instrPath);\n } else if (state?.originalInstrumentationContent !== undefined) {\n // Restore the exact original content to avoid removing\n // pre-existing imports that removeRegisterGlasstrace would strip.\n fs.writeFileSync(instrPath, state.originalInstrumentationContent, \"utf-8\");\n } else {\n const cleaned = removeRegisterGlasstrace(content);\n if (cleaned !== content) {\n fs.writeFileSync(instrPath, cleaned, \"utf-8\");\n }\n }\n }\n break;\n }\n case \"next-config\": {\n for (const name of NEXT_CONFIG_NAMES) {\n const configPath = path.join(projectRoot, name);\n if (!fs.existsSync(configPath)) {\n continue;\n }\n const content = fs.readFileSync(configPath, \"utf-8\");\n if (!content.includes(\"withGlasstraceConfig\")) {\n continue;\n }\n const isESM = name.endsWith(\".ts\") || name.endsWith(\".mjs\");\n const unwrapResult = isESM\n ? unwrapExport(content)\n : unwrapCJSExport(content);\n if (unwrapResult.unwrapped) {\n const cleaned = removeGlasstraceConfigImport(unwrapResult.content);\n fs.writeFileSync(configPath, cleanLeadingBlankLines(cleaned), \"utf-8\");\n }\n break;\n }\n break;\n }\n case \"env-local\": {\n // Only remove GLASSTRACE_API_KEY lines — scaffoldEnvLocal (step 5)\n // only adds the API key. Removing GLASSTRACE_COVERAGE_MAP here would\n // delete a user's pre-existing coverage map setting if init fails\n // after step 5 but before the coverage map step.\n const envPath = path.join(projectRoot, \".env.local\");\n if (fs.existsSync(envPath)) {\n const content = fs.readFileSync(envPath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const filtered = lines.filter((line) => {\n const trimmed = line.trim();\n return !/^\\s*#?\\s*GLASSTRACE_API_KEY\\s*=/.test(trimmed);\n });\n if (filtered.length !== lines.length) {\n const result = filtered.join(\"\\n\");\n if (result.trim().length === 0) {\n fs.unlinkSync(envPath);\n } else {\n fs.writeFileSync(envPath, result, \"utf-8\");\n }\n }\n }\n break;\n }\n case \"gitignore\": {\n const gitignorePath = path.join(projectRoot, \".gitignore\");\n if (fs.existsSync(gitignorePath)) {\n const content = fs.readFileSync(gitignorePath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const filtered = lines.filter(\n (line) => line.trim() !== \".glasstrace/\",\n );\n if (filtered.length !== lines.length) {\n const result = filtered.join(\"\\n\");\n if (result.trim().length === 0) {\n fs.unlinkSync(gitignorePath);\n } else {\n fs.writeFileSync(gitignorePath, result, \"utf-8\");\n }\n }\n }\n break;\n }\n case \"discovery-file\": {\n // Remove the static discovery file scaffolded in Step 7. The\n // removeDiscoveryFile helper also best-effort removes an empty\n // `.well-known/` directory but never touches sibling content.\n removeDiscoveryFile(projectRoot);\n break;\n }\n }\n } catch {\n // Best-effort rollback — log nothing, continue with remaining steps\n }\n }\n}\n\n/**\n * Core init logic. Exported for testability — the CLI entry point at the\n * bottom calls this function and translates the result to process.exit().\n */\nexport async function runInit(options: InitOptions): Promise<InitResult> {\n const { yes, coverageMap } = options;\n const summary: string[] = [];\n const warnings: string[] = [];\n const errors: string[] = [];\n\n // Step 0: Resolve the correct project root (monorepo awareness)\n let projectRoot: string;\n try {\n const classification = resolveProjectRoot(options.projectRoot);\n projectRoot = classification.projectRoot;\n if (classification.isMonorepo && classification.appRelativePath) {\n summary.push(`Found Next.js app at ${classification.appRelativePath} — installing there`);\n }\n } catch (err) {\n errors.push(err instanceof Error ? err.message : String(err));\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 1: Detect package.json\n const packageJsonPath = path.join(projectRoot, \"package.json\");\n if (!fs.existsSync(packageJsonPath)) {\n errors.push(\"No package.json found. Run this command from a Node.js project root.\");\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Track completed steps so we can roll them back if a later step fails.\n // Only steps that modify the filesystem are tracked — pre-existing state\n // (e.g., \"already-registered\") is never rolled back.\n const rollbackState: RollbackState = { steps: [] };\n\n // Step 2: Ensure the instrumentation file has registerGlasstrace().\n // DISC-493 Issue 1: detect `src/` layout and merge into the existing file\n // rather than overwriting the project root.\n try {\n // Pre-resolve the target so we can save the original content (for\n // faithful rollback) and record the path for the rollback state.\n const preResolved = resolveInstrumentationTarget(projectRoot);\n if (!preResolved.conflict && preResolved.target !== null) {\n rollbackState.instrumentationPath = preResolved.target;\n if (fs.existsSync(preResolved.target)) {\n rollbackState.originalInstrumentationContent = fs.readFileSync(\n preResolved.target,\n \"utf-8\",\n );\n }\n }\n\n const instrResult = await scaffoldInstrumentation(projectRoot, {\n // `--yes` implies non-interactive automation and must not hang on a\n // merge confirmation prompt. `--force` skips the prompt explicitly\n // (DISC-1247 Scenario 2c parity).\n force: options.force === true || options.yes,\n });\n // Record the exact path the scaffolder wrote to, in case the resolver\n // and scaffolder ever disagree (symlinks, TOCTOU) — rollback is more\n // accurate when it targets the file that was actually mutated.\n if (instrResult.filePath !== undefined) {\n rollbackState.instrumentationPath = instrResult.filePath;\n }\n const relativePath =\n instrResult.filePath !== undefined\n ? path.relative(projectRoot, instrResult.filePath)\n : \"instrumentation.ts\";\n switch (instrResult.action) {\n case \"created\":\n summary.push(`Created ${relativePath}`);\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"injected\":\n summary.push(`Added registerGlasstrace() to existing ${relativePath}`);\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"appended\":\n summary.push(\n `Appended register() with registerGlasstrace() to ${relativePath}`,\n );\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"already-registered\":\n summary.push(`Skipped ${relativePath} (registerGlasstrace already present)`);\n break;\n case \"skipped\":\n // User declined the merge prompt (DISC-1247 Scenario 2c parity).\n // Emit a warning so re-init output makes clear nothing changed.\n // \"--force\" here only bypasses the confirmation — the scaffolder\n // merges rather than overwriting, so the wording is deliberate.\n warnings.push(\n `Preserved ${relativePath} (merge declined; re-run with --force to apply the merge without prompting)`,\n );\n break;\n case \"conflict\": {\n // Both root and src/ instrumentation files exist — Next.js's\n // loader behavior is undefined (DISC-493 Issue 1). Refuse to\n // write a third competing file; point the user at the file to\n // merge into and tell them to remove the other.\n const primary =\n instrResult.filePath !== undefined\n ? path.relative(projectRoot, instrResult.filePath)\n : \"src/instrumentation.ts\";\n const competing =\n instrResult.conflictingPath !== undefined\n ? path.relative(projectRoot, instrResult.conflictingPath)\n : \"instrumentation.ts\";\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(\n `Both ${primary} and ${competing} exist. Next.js's loader behavior is undefined when both are present.\\n` +\n `Merge your instrumentation into ${primary} and remove ${competing}, then re-run init.`,\n );\n return { exitCode: 1, summary, warnings, errors };\n }\n case \"unrecognized\":\n warnings.push(\n `${relativePath} exists but has no recognizable register() function.\\n` +\n \"Add this import at the top of your file:\\n\\n\" +\n ' import { registerGlasstrace } from \"@glasstrace/sdk\";\\n\\n' +\n \"Then add this as the first statement in your register() function:\\n\\n\" +\n \" registerGlasstrace();\\n\",\n );\n break;\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to write instrumentation file: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 4: Detect and wrap next.config.*\n try {\n const configResult = await scaffoldNextConfig(projectRoot);\n if (configResult?.modified) {\n summary.push(\"Wrapped next.config with withGlasstraceConfig()\");\n rollbackState.steps.push(\"next-config\");\n } else if (configResult === null) {\n warnings.push(\"No next.config.* found. You may need to create one for Next.js projects.\");\n } else if (configResult.reason === \"already-wrapped\") {\n summary.push(\"Skipped next.config (already contains withGlasstraceConfig)\");\n } else if (configResult.reason === \"empty-file\") {\n warnings.push(\"next.config is empty — add a Next.js configuration export to enable wrapping\");\n } else {\n warnings.push(\"next.config has no recognizable export pattern — add withGlasstraceConfig() manually\");\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to modify next.config: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 5: Update .env.local\n // DISC-1247 Scenario 6: if .env.local already defines a claimed\n // developer key (gt_dev_*), scaffoldEnvLocal preserves it and this\n // step reports the preservation so the user knows re-init did not\n // overwrite their claim.\n try {\n const envPathForCheck = path.join(projectRoot, \".env.local\");\n let existingDevKey = false;\n if (fs.existsSync(envPathForCheck)) {\n const existingContent = fs.readFileSync(envPathForCheck, \"utf-8\");\n existingDevKey = isDevApiKey(readEnvLocalApiKey(existingContent));\n }\n const envCreated = await scaffoldEnvLocal(projectRoot);\n if (envCreated) {\n summary.push(\"Updated .env.local with Glasstrace configuration\");\n rollbackState.steps.push(\"env-local\");\n } else if (existingDevKey) {\n summary.push(\n \"Preserved existing .env.local (GLASSTRACE_API_KEY contains a claimed dev key)\",\n );\n } else {\n summary.push(\"Skipped .env.local (GLASSTRACE_API_KEY already configured)\");\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to update .env.local: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 6: Update .gitignore\n try {\n const gitignoreUpdated = await scaffoldGitignore(projectRoot);\n if (gitignoreUpdated) {\n summary.push(\"Updated .gitignore with .glasstrace/\");\n rollbackState.steps.push(\"gitignore\");\n } else {\n summary.push(\"Skipped .gitignore (.glasstrace/ already listed)\");\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to update .gitignore: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 7: MCP auto-configuration\n // Use CI env vars (not TTY check) to distinguish automated builds from\n // manual CLI usage. TTY state is unreliable — piped output, test runners,\n // and IDE terminals all report isTTY=false despite being user-initiated.\n // Accept any truthy CI value (GitHub Actions, GitLab, CircleCI, Travis,\n // etc.) and also check GITHUB_ACTIONS specifically.\n const ciEnv = process.env[\"CI\"];\n const isCI =\n (typeof ciEnv === \"string\" &&\n ciEnv.trim() !== \"\" &&\n ciEnv.toLowerCase() !== \"false\" &&\n ciEnv.trim() !== \"0\") ||\n process.env[\"GITHUB_ACTIONS\"] === \"true\";\n\n try {\n // DISC-1247 Scenario 2a: preserve any existing anonymous key.\n // getOrCreateAnonKey already reads an existing key if present, so\n // re-running init never overwrites a key that may be linked to an\n // account. We explicitly check first so we can report the preservation\n // in the summary — without this, users have no feedback that re-init\n // respected their existing claim linkage.\n const preExistingAnonKey = await readAnonKey(projectRoot);\n const anonKey = await getOrCreateAnonKey(projectRoot);\n if (preExistingAnonKey !== null) {\n summary.push(\"Preserved existing .glasstrace/anon_key\");\n }\n\n // Step 7a: Write the static discovery file at\n // `<staticRoot>/.well-known/glasstrace.json` so the Glasstrace browser\n // extension can discover the project's anon key without a runtime HTTP\n // handler (design doc \"SDK Discovery Endpoint / Static File\" §6.1).\n //\n // Re-init (DISC-1247 Scenario 2) preserves any user-added fields and\n // only rewrites when the on-disk key no longer matches. A failed\n // write is surfaced as a warning rather than an init-blocking error:\n // scaffolding has already succeeded at this point and the user can\n // unblock themselves by inspecting the path and re-running.\n try {\n const discoveryResult = writeDiscoveryFile(projectRoot, anonKey);\n const relPath = relativeDiscoveryPath(discoveryResult.layout);\n switch (discoveryResult.action) {\n case \"created\":\n summary.push(`Created ${relPath}`);\n rollbackState.steps.push(\"discovery-file\");\n break;\n case \"updated-stale\":\n summary.push(`Updated ${relPath} (anon key had changed)`);\n // Not pushed to rollback: the pre-existing file was user-owned\n // content (just with a stale key). A rollback should restore\n // the original key rather than delete the file outright, and\n // we do not snapshot the prior content. Leaving it alone is\n // the safer behavior.\n break;\n case \"skipped-matches\":\n summary.push(`Skipped ${relPath} (already matches anon key)`);\n break;\n case \"skipped-foreign\":\n summary.push(\n `Rewrote ${relPath} (existing file was malformed or not SDK-managed)`,\n );\n // Same reasoning as \"updated-stale\": we did not snapshot the\n // original content, so rollback can only delete. Leaving off\n // the rollback list prevents data loss on a later-step failure.\n break;\n case \"failed\":\n warnings.push(\n `Failed to write ${relPath}${\n discoveryResult.error !== undefined\n ? `: ${discoveryResult.error}`\n : \"\"\n }. The Glasstrace browser extension will fall back to the runtime handler until the file is written.`,\n );\n break;\n }\n\n // Emit a warning if the user's `.gitignore` excludes `.well-known/`\n // or the discovery file path — otherwise the file will not be\n // deployed and the extension will silently fail to discover the\n // project. We never modify `.gitignore` (the file is public\n // metadata, not a secret).\n const gitignorePath = path.join(projectRoot, \".gitignore\");\n if (fs.existsSync(gitignorePath)) {\n try {\n const gitignoreContent = fs.readFileSync(gitignorePath, \"utf-8\");\n if (gitignoreExcludesDiscoveryFile(gitignoreContent, discoveryResult.layout)) {\n warnings.push(\n `Your .gitignore excludes ${relPath} (directly or via a parent rule). ` +\n \"The discovery file must be committed for the Glasstrace browser extension to find it in deployed builds. \" +\n \"Remove the matching line from .gitignore or add an explicit negation (e.g. `!\" +\n relPath +\n \"`).\",\n );\n }\n } catch {\n // Unreadable .gitignore is not actionable here — skip the check.\n }\n }\n } catch (err) {\n warnings.push(\n `Failed to write ${relativeDiscoveryPath(\n resolveStaticRoot(projectRoot).layout,\n )}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n // `anyConfigWritten` covers both fresh writes AND user-preserved\n // configs — it gates nudge suppression (we don't want to pester\n // users about MCP setup they consciously preserved) and the\n // gitignore update.\n //\n // `anyConfigRewrittenWithBearer` is stricter: it tracks whether\n // this invocation actually wrote new content with the resolved\n // bearer. The marker is gated on this stricter flag so we don't\n // stamp the marker as \"this credential is what's on disk\" when the\n // user declined an overwrite — that would let a later\n // `glasstrace mcp add` falsely conclude the project is already\n // configured for the new credential and skip the refresh\n // (DISC-1512).\n let anyConfigWritten = false;\n let anyConfigRewrittenWithBearer = false;\n\n // Resolve the effective MCP credential. Managed configs embed\n // whichever bearer the project is authenticating ingestion with —\n // anon key on first init, dev key on a re-init after an account\n // claim. The discovery file at `.well-known/glasstrace.json`\n // continues to use the anon key (it is a public project\n // identifier read by the browser extension, not an auth token).\n const resolved = await resolveEffectiveMcpCredential(projectRoot);\n const bearer = resolved.effective?.key ?? anonKey;\n\n if (isCI) {\n // Non-interactive: write only the generic .glasstrace/mcp.json.\n // CI uses `force: true` for MCP diff decisions because there's no\n // interactive terminal to prompt on — existing configs in CI\n // workspaces are rare and safe to overwrite.\n const genericAgent: DetectedAgent = {\n name: \"generic\",\n mcpConfigPath: path.join(projectRoot, \".glasstrace\", \"mcp.json\"),\n infoFilePath: null,\n cliAvailable: false,\n registrationCommand: null,\n };\n const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, bearer);\n const decision = await decideMcpConfigAction({\n configPath: genericAgent.mcpConfigPath,\n expectedContent: genericConfig,\n force: true,\n });\n if (decision !== \"skip\") {\n await writeMcpConfig(genericAgent, genericConfig, projectRoot);\n if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigRewrittenWithBearer = true;\n }\n }\n if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigWritten = true;\n summary.push(\"Created .glasstrace/mcp.json (CI mode)\");\n }\n } else {\n // Interactive: detect agents and configure each\n let agents: DetectedAgent[];\n try {\n agents = await detectAgents(projectRoot);\n } catch (detectErr) {\n warnings.push(\n `Agent detection failed: ${detectErr instanceof Error ? detectErr.message : String(detectErr)}. Writing generic config only.`,\n );\n // Fall back to generic-only config\n const genericAgent: DetectedAgent = {\n name: \"generic\",\n mcpConfigPath: path.join(projectRoot, \".glasstrace\", \"mcp.json\"),\n infoFilePath: null,\n cliAvailable: false,\n registrationCommand: null,\n };\n const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, bearer);\n await writeMcpConfig(genericAgent, genericConfig, projectRoot);\n if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigWritten = true;\n anyConfigRewrittenWithBearer = true;\n }\n agents = [];\n }\n\n const configuredNames: string[] = [];\n\n for (const agent of agents) {\n try {\n const configContent = generateMcpConfig(agent, MCP_ENDPOINT, bearer);\n\n // Diff-aware MCP write (DISC-1247 Scenario 2c): if the existing\n // config differs from what init would write, prompt before\n // overwriting. `--force` (or --yes in non-interactive mode)\n // skips the prompt.\n const decision = await decideMcpConfigAction({\n configPath: agent.mcpConfigPath,\n expectedContent: configContent,\n force: options.force === true || options.yes,\n });\n\n if (decision === \"skip\") {\n summary.push(\n `Preserved existing ${agent.mcpConfigPath ?? agent.name} (user declined overwrite)`,\n );\n if (agent.mcpConfigPath !== null && fs.existsSync(agent.mcpConfigPath)) {\n // Count existing user-edited config as \"present\" so the\n // marker file still gets written — otherwise nudges would\n // nag the user about MCP setup they consciously preserved.\n anyConfigWritten = true;\n }\n continue;\n }\n\n await writeMcpConfig(agent, configContent, projectRoot);\n\n // Verify the config file was actually written (writeMcpConfig\n // swallows permission errors and returns void)\n const configExists = agent.mcpConfigPath !== null && fs.existsSync(agent.mcpConfigPath);\n if (!configExists) {\n continue;\n }\n\n anyConfigWritten = true;\n anyConfigRewrittenWithBearer = true;\n\n const infoContent = generateInfoSection(agent, MCP_ENDPOINT);\n if (infoContent !== \"\") {\n await injectInfoSection(agent, infoContent, projectRoot);\n }\n\n if (agent.name !== \"generic\") {\n configuredNames.push(formatAgentName(agent.name));\n }\n } catch (agentErr) {\n warnings.push(\n `Failed to configure MCP for ${agent.name}: ${agentErr instanceof Error ? agentErr.message : String(agentErr)}`,\n );\n }\n }\n\n if (configuredNames.length > 0) {\n summary.push(`Configured MCP for: ${configuredNames.join(\", \")}`);\n } else if (anyConfigWritten) {\n summary.push(\"Created .glasstrace/mcp.json (generic config)\");\n }\n }\n\n // Add MCP config files to .gitignore\n await updateGitignore(\n [\".mcp.json\", \".cursor/mcp.json\", \".gemini/settings.json\", \".codex/config.toml\"],\n projectRoot,\n );\n\n // Create marker file only if this invocation actually wrote a\n // config with the resolved bearer. We intentionally use the\n // stricter `anyConfigRewrittenWithBearer` flag instead of the\n // looser `anyConfigWritten`: stamping the marker for a credential\n // we did not actually write to disk would let a later\n // `glasstrace mcp add` short-circuit on a stale config, missing\n // the claim-transition refresh (DISC-1512). When all configs were\n // user-preserved, no marker is written — the existing on-disk\n // marker (if any) stays untouched so its source/hash continues to\n // describe what was actually written previously.\n if (anyConfigRewrittenWithBearer) {\n const markerSource = resolved.effective?.source ?? \"anon\";\n const markerHash = identityFingerprint(bearer);\n const markerCreated = await writeMcpMarker(projectRoot, {\n credentialSource: markerSource,\n credentialHash: markerHash,\n });\n if (markerCreated) {\n summary.push(\"Created .glasstrace/mcp-connected marker\");\n }\n }\n } catch (mcpErr) {\n warnings.push(\n `MCP auto-configuration failed: ${mcpErr instanceof Error ? mcpErr.message : String(mcpErr)}`,\n );\n }\n\n // Step 8: Coverage map opt-in\n let enableCoverageMap = coverageMap;\n if (!yes && !coverageMap) {\n if (process.stdin.isTTY) {\n enableCoverageMap = await promptYesNo(\n \"Would you like to enable test coverage mapping?\",\n false,\n );\n }\n }\n\n if (enableCoverageMap) {\n try {\n const added = await addCoverageMapEnv(projectRoot);\n if (added) {\n summary.push(\"Added GLASSTRACE_COVERAGE_MAP=true to .env.local\");\n }\n } catch (err) {\n warnings.push(`Failed to add coverage map env: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n // Step 9: Run initial import graph scan\n try {\n await buildImportGraph(projectRoot);\n summary.push(\"Completed initial import graph scan\");\n } catch (err) {\n warnings.push(`Import graph scan failed: ${err instanceof Error ? err.message : String(err)}. You can run it later.`);\n }\n }\n\n // Step 10: Blocking verification that the anon key is registered\n // server-side (DISC-493 Issue 3, DISC-494). Runs last so failures here\n // surface the exit code without blocking the file scaffolding work.\n //\n // Skipped when:\n // - `GLASSTRACE_SKIP_INIT_VERIFY=1` is set (offline installs)\n // - CI mode is active (CI builds should not reach external networks)\n // - Vitest is running (`VITEST` env var) — unit tests that don't\n // care about the network path shouldn't be forced to mock the\n // transport; tests that DO care use `_setTransportForTesting`\n // and explicitly un-set `VITEST` before calling runInit.\n const skipVerify =\n process.env[\"GLASSTRACE_SKIP_INIT_VERIFY\"] === \"1\" ||\n process.env[\"GLASSTRACE_SKIP_INIT_VERIFY\"] === \"true\" ||\n process.env[\"VITEST\"] === \"true\";\n\n if (!skipVerify && !isCI) {\n const verifyResult = await verifyAnonKeyRegistration(projectRoot);\n if (verifyResult.outcome === \"failed\") {\n errors.push(verifyResult.error);\n return { exitCode: 2, summary, warnings, errors };\n }\n if (verifyResult.outcome === \"verified\") {\n summary.push(\"Verified anon key registration with Glasstrace API\");\n } else {\n // \"skipped\": no anon key on disk means MCP auto-configuration\n // failed earlier (a warning was already emitted). Reporting\n // \"Verified ...\" here would misrepresent the state of setup —\n // a verification request was never even sent.\n summary.push(\"Skipped anon key verification (no anon key on disk)\");\n }\n }\n\n return { exitCode: 0, summary, warnings, errors };\n}\n\n/**\n * Outcome of {@link verifyAnonKeyRegistration}:\n *\n * - `\"verified\"` — the server registered the anon key (HTTP 2xx with a\n * schema-valid response body).\n * - `\"skipped\"` — no anon key was on disk, so no verification request\n * was sent. Typically means MCP auto-configuration failed earlier\n * (warnings were already emitted) — distinct from a verification\n * success.\n * - `\"failed\"` — the verification request was sent and failed.\n * `error` is a user-facing message distinguishing the failure class\n * (`fetch failed: ...`, `server rejected the key ...`, or\n * `server returned malformed response ...`) with no anon key bytes.\n */\nexport type VerifyAnonKeyOutcome =\n | { outcome: \"verified\" }\n | { outcome: \"skipped\" }\n | { outcome: \"failed\"; error: string };\n\n/**\n * Verifies that the anonymous key written by init is registered\n * server-side. Called at the end of the scaffold flow so that when it\n * fails, the user sees an actionable error rather than a misleading\n * \"initialized successfully\" followed by silent MCP authentication\n * failures (DISC-494).\n *\n * Returns a discriminated outcome so the CLI can distinguish a genuine\n * verification pass from the \"no anon key on disk\" skip case. On\n * failure the error message distinguishes three classes:\n * - \"fetch failed\" — transport error (DNS, TCP, TLS, timeout)\n * - \"server rejected the key\" — HTTP 4xx/5xx\n * - \"server returned malformed response\" — HTTP 2xx with unparseable\n * body\n *\n * The anon key is NEVER included in the returned message. Callers\n * (the CLI entry point) render it verbatim to stderr via the errors\n * array and exit non-zero.\n *\n * @internal Exported for testability.\n */\nexport async function verifyAnonKeyRegistration(\n projectRoot: string,\n): Promise<VerifyAnonKeyOutcome> {\n // Read env the same way the runtime would — scaffoldEnvLocal may have\n // written GLASSTRACE_API_KEY=gt_anon_..., so prefer the generated\n // anon key on disk as the authoritative value to verify.\n const anonKey = await readAnonKey(projectRoot);\n if (anonKey === null) {\n // No anon key on disk means MCP wasn't configured. Skip\n // verification — this is a partial install, not a verification\n // failure. The init flow already emitted warnings above. Report\n // \"skipped\" so the caller doesn't claim verification succeeded.\n return { outcome: \"skipped\" };\n }\n\n // Load the dev key from .env.local if present (for straggler linking)\n // but fall through to anon-only verification otherwise. Reading is\n // best-effort: any error leaves devKey undefined and we verify the\n // anon key in isolation.\n //\n // Use `readEnvLocalApiKey` (shared with the rest of the init flow) so\n // we match dotenv-style last-wins semantics. A hand-rolled regex here\n // would pick the FIRST `GLASSTRACE_API_KEY=` assignment, which in a\n // rotated env file would authenticate with a stale key and surface a\n // false `server rejected` failure even when the effective key is\n // valid.\n let devKey: string | undefined;\n try {\n const envPath = path.join(projectRoot, \".env.local\");\n if (fs.existsSync(envPath)) {\n const envContent = fs.readFileSync(envPath, \"utf-8\");\n const effective = readEnvLocalApiKey(envContent);\n // Only send a real dev key in the dev slot — don't double-count\n // the anon key as both keys.\n if (effective !== null && isDevApiKey(effective)) {\n devKey = effective;\n }\n }\n } catch {\n // Ignore — fall through to anon-only verification.\n }\n\n // Resolve endpoint / environment from the user's shell, then pin\n // `apiKey` to whatever we read from .env.local. `resolveConfig` uses\n // `??` to fall back to `process.env.GLASSTRACE_API_KEY` when the\n // option is undefined, which would cause verification to authenticate\n // with an unrelated stale key in shells that export\n // `GLASSTRACE_API_KEY` — producing false \"server rejected\" failures\n // even when the freshly generated anon key is valid. The explicit\n // override keeps verification tied to disk state.\n const baseConfig = resolveConfig({ apiKey: devKey });\n const config = { ...baseConfig, apiKey: devKey };\n const sdkVersion = typeof __SDK_VERSION__ === \"string\" ? __SDK_VERSION__ : \"0.0.0-dev\";\n\n const result: VerifyInitResult = await verifyInitReachable(config, anonKey, sdkVersion);\n\n if (result.ok) {\n return { outcome: \"verified\" };\n }\n\n const hint = \"Run 'npx glasstrace status' or 'npx glasstrace doctor' to diagnose.\";\n switch (result.reason) {\n case \"transport\":\n // `result.detail` is the raw cause with any leading `fetch failed: `\n // stripped by verifyInitReachable. Format once here so the output\n // matches the documented `fetch failed: <reason>` shape and never\n // renders `fetch failed (fetch failed: ...)`.\n return {\n outcome: \"failed\",\n error: `Glasstrace init verification failed: fetch failed: ${result.detail}. ${hint}`,\n };\n case \"rejected\":\n return {\n outcome: \"failed\",\n error: `Glasstrace init verification failed: server rejected the key (HTTP ${result.status}). ${hint}`,\n };\n case \"malformed\":\n return {\n outcome: \"failed\",\n error: `Glasstrace init verification failed: server returned malformed response. ${hint}`,\n };\n }\n}\n\n/**\n * Parses CLI arguments into InitOptions.\n */\nfunction parseArgs(argv: string[]): InitOptions {\n const args = argv.slice(2); // skip node + script path\n let yes = false;\n let coverageMap = false;\n let force = false;\n\n for (const arg of args) {\n if (arg === \"--yes\" || arg === \"-y\") {\n yes = true;\n } else if (arg === \"--coverage-map\") {\n coverageMap = true;\n } else if (arg === \"--force\") {\n force = true;\n }\n }\n\n // Auto-detect non-interactive\n if (!process.stdin.isTTY) {\n yes = true;\n }\n\n return {\n projectRoot: process.cwd(),\n yes,\n coverageMap,\n force,\n };\n}\n\n/**\n * CLI entry point. Only runs when this module is executed directly\n * (not when imported for testing).\n */\nconst scriptPath =\n typeof process !== \"undefined\" && process.argv[1] !== undefined\n ? process.argv[1].replace(/\\\\/g, \"/\")\n : undefined;\n\nconst scriptBasename = scriptPath !== undefined ? path.basename(scriptPath) : undefined;\n\nconst isDirectExecution =\n scriptPath !== undefined &&\n (scriptPath.endsWith(\"/cli/init.js\") ||\n scriptPath.endsWith(\"/cli/init.ts\") ||\n scriptBasename === \"glasstrace\");\n\nif (isDirectExecution) {\n // Enforce minimum Node.js version before any command processing.\n // The engines field in package.json is advisory — npm does not enforce\n // it by default, so this provides a clear error for users on older runtimes.\n if (!meetsNodeVersion(20)) {\n process.stderr.write(\n `Error: @glasstrace/sdk requires Node.js >= 20. Current version: ${process.version}\\n`,\n );\n process.exit(1);\n }\n\n const subcommand = process.argv[2];\n\n if (subcommand === \"mcp\") {\n if (process.argv[3] === \"add\") {\n // Parse --force and --dry-run from remaining args\n const remainingArgs = process.argv.slice(4);\n const force = remainingArgs.includes(\"--force\");\n const dryRun = remainingArgs.includes(\"--dry-run\");\n\n import(\"./mcp-add.js\")\n .then(({ mcpAdd }) => mcpAdd({ force, dryRun }))\n .then((result) => {\n for (const msg of result.messages) {\n process.stderr.write(msg + \"\\n\");\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else {\n process.stderr.write(\n `Unknown mcp subcommand: ${process.argv[3] ?? \"(none)\"}\\n\\n` +\n \"Usage: glasstrace mcp add [--force] [--dry-run]\\n\",\n );\n process.exit(1);\n }\n } else if (subcommand === undefined || subcommand === \"init\" || subcommand.startsWith(\"-\")) {\n // Default: run init (handles `glasstrace`, `glasstrace init`, `glasstrace --yes`)\n const forwardedArgs = process.argv.slice(subcommand === \"init\" ? 3 : 2);\n\n // `--validate` is an init sub-mode that checks artifact consistency\n // without scaffolding (DISC-1247 Scenario 4). We dispatch to a\n // dedicated module so the main init path stays unburdened.\n //\n // Resolve the app root via the same monorepo-aware logic that\n // `runInit` and `runStatus` use so validation in a monorepo root\n // inspects the actual Next.js app directory rather than the empty\n // workspace root (addresses Codex P2 review feedback).\n if (forwardedArgs.includes(\"--validate\")) {\n let validateProjectRoot = process.cwd();\n try {\n validateProjectRoot = resolveProjectRoot(validateProjectRoot).projectRoot;\n } catch {\n // Fall back to cwd if the monorepo resolver can't find an app —\n // validate can still report orphan-artifact issues at the raw\n // cwd and will exit non-zero rather than hiding the problem.\n }\n import(\"./validate.js\")\n .then(({ runValidate }) => runValidate({ projectRoot: validateProjectRoot }))\n .then((result) => {\n for (const line of result.summary) {\n process.stderr.write(`${line}\\n`);\n }\n for (const issue of result.issues) {\n process.stderr.write(` - ${issue.message}\\n`);\n if (issue.fix) {\n process.stderr.write(` Fix: ${issue.fix}\\n`);\n }\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else {\n const options = parseArgs(process.argv);\n\n runInit(options)\n .then((result) => {\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n process.stderr.write(`Error: ${err}\\n`);\n }\n }\n if (result.warnings.length > 0) {\n for (const warn of result.warnings) {\n process.stderr.write(`Warning: ${warn}\\n`);\n }\n }\n if (result.summary.length > 0) {\n // Only claim success when exitCode is 0. A non-zero exit\n // means init scaffolding completed but a later step (e.g.,\n // verifyAnonKeyRegistration) failed — we must not tell the\n // user everything is fine (DISC-494 root cause).\n if (result.exitCode === 0) {\n process.stderr.write(\"\\nGlasstrace initialized successfully!\\n\\n\");\n } else {\n process.stderr.write(\"\\nGlasstrace init completed with errors.\\n\\n\");\n }\n for (const line of result.summary) {\n process.stderr.write(` - ${line}\\n`);\n }\n if (result.exitCode === 0) {\n process.stderr.write(\"\\nNext steps:\\n\");\n process.stderr.write(\" 1. Start your Next.js dev server\\n\");\n process.stderr.write(\n \" 2. Glasstrace works immediately in anonymous mode\\n\",\n );\n process.stderr.write(\n \" 3. To link to your account, set GLASSTRACE_API_KEY in .env.local\\n\\n\",\n );\n }\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n }\n } else if (subcommand === \"uninit\") {\n const remainingArgs = process.argv.slice(3);\n const dryRun = remainingArgs.includes(\"--dry-run\");\n const force = remainingArgs.includes(\"--force\");\n\n import(\"./uninit.js\")\n .then(({ runUninit }) => runUninit({ projectRoot: process.cwd(), dryRun, force }))\n .then((result) => {\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n process.stderr.write(`Error: ${err}\\n`);\n }\n }\n if (result.warnings.length > 0) {\n for (const warn of result.warnings) {\n process.stderr.write(`Warning: ${warn}\\n`);\n }\n }\n if (result.summary.length > 0) {\n process.stderr.write(\"\\n\");\n for (const line of result.summary) {\n process.stderr.write(` ${line}\\n`);\n }\n process.stderr.write(\"\\n\");\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else if (subcommand === \"status\") {\n const remainingArgs = process.argv.slice(3);\n const json = remainingArgs.includes(\"--json\");\n\n Promise.all([import(\"./status.js\"), import(\"./monorepo.js\")])\n .then(([{ runStatus }, { resolveProjectRoot: resolve }]) => {\n let projectRoot = process.cwd();\n try {\n projectRoot = resolve(projectRoot).projectRoot;\n } catch {\n // Fall back to cwd if monorepo resolution fails\n }\n const result = runStatus({ projectRoot });\n if (json) {\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n } else {\n const checks = [\n [\"Installed\", result.installed],\n [\"Initialized\", result.initialized],\n [\"Instrumentation\", result.instrumentation],\n [\"Config wrapped\", result.configWrapped],\n [\"Anon key\", result.anonKey],\n [\"MCP configured\", result.mcpConfigured],\n ] as const;\n for (const [label, ok] of checks) {\n process.stderr.write(` ${ok ? \"+\" : \"-\"} ${label}\\n`);\n }\n if (result.agents.length > 0) {\n process.stderr.write(` + Agents: ${result.agents.join(\", \")}\\n`);\n } else {\n process.stderr.write(\" - Agents\\n\");\n }\n }\n process.exit(0);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else {\n process.stderr.write(\n `Unknown command: ${subcommand}\\n\\n` +\n \"Usage:\\n\" +\n \" glasstrace init [--yes] [--coverage-map] [--force] [--validate]\\n\" +\n \" glasstrace uninit [--dry-run] [--force]\\n\" +\n \" glasstrace status [--json]\\n\" +\n \" glasstrace mcp add [--force] [--dry-run]\\n\",\n );\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/cli/init.ts","../../src/cli/scaffolder.ts"],"sourcesContent":["#!/usr/bin/env node\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as readline from \"node:readline\";\nimport {\n scaffoldInstrumentation,\n scaffoldNextConfig,\n scaffoldEnvLocal,\n scaffoldGitignore,\n addCoverageMapEnv,\n resolveInstrumentationTarget,\n} from \"./scaffolder.js\";\nimport {\n identityFingerprint,\n isDevApiKey,\n MCP_ENDPOINT,\n mcpConfigMatches,\n readEnvLocalApiKey,\n resolveEffectiveMcpCredential,\n writeMcpMarker,\n} from \"../mcp-runtime.js\";\nimport { buildImportGraph } from \"../import-graph.js\";\nimport { getOrCreateAnonKey, readAnonKey } from \"../anon-key.js\";\nimport { detectAgents } from \"../agent-detection/detect.js\";\nimport { generateMcpConfig, generateInfoSection } from \"../agent-detection/configs.js\";\nimport { writeMcpConfig, injectInfoSection, updateGitignore } from \"../agent-detection/inject.js\";\nimport type { DetectedAgent } from \"../agent-detection/detect.js\";\nimport { NEXT_CONFIG_NAMES, formatAgentName } from \"./constants.js\";\nimport { resolveProjectRoot } from \"./monorepo.js\";\nimport {\n isInitCreatedInstrumentation,\n removeRegisterGlasstrace,\n unwrapExport,\n unwrapCJSExport,\n removeGlasstraceConfigImport,\n} from \"./uninit.js\";\nimport { verifyInitReachable, type VerifyInitResult } from \"../init-client.js\";\nimport { resolveConfig } from \"../env-detection.js\";\nimport {\n writeDiscoveryFile,\n removeDiscoveryFile,\n relativeDiscoveryPath,\n resolveStaticRoot,\n} from \"./discovery-file.js\";\n\n// Declare the tsup-injected SDK version literal. Replaced at build time\n// via `define` in tsup.config.ts. Falls back to \"0.0.0-dev\" when\n// running tests under vitest (no tsup build step).\ndeclare const __SDK_VERSION__: string;\n\n/**\n * Returns true if the current Node.js major version meets the minimum requirement.\n * Exported for testability — the CLI entry point uses this to gate execution.\n */\nexport function meetsNodeVersion(minMajor: number): boolean {\n const [major] = process.versions.node.split(\".\").map(Number);\n return major >= minMajor;\n}\n\n/** Options for the init command (parsed from CLI args or passed programmatically). */\nexport interface InitOptions {\n projectRoot: string;\n yes: boolean;\n coverageMap: boolean;\n /**\n * When true, skip interactive confirmation and overwrite existing\n * MCP configuration files without prompting. Preservation of the\n * anonymous key, config cache, and developer API key still applies\n * regardless of this flag — `--force` only affects the MCP diff\n * prompt (DISC-1247 Scenario 2c). Defaults to `false`.\n */\n force?: boolean;\n}\n\n/** Result of running the init command. */\nexport interface InitResult {\n exitCode: number;\n summary: string[];\n warnings: string[];\n errors: string[];\n}\n\n/**\n * Decides whether the MCP config at `configPath` should be overwritten\n * during re-init. Returns the action to take.\n *\n * - `\"write\"` — file does not exist, or existing content already matches\n * the expected content. Safe to write.\n * - `\"skip\"` — existing file differs AND the user chose to keep it, or\n * we are in a non-interactive environment without `--force`.\n * - `\"force-overwrite\"` — `force === true` (or user accepted the prompt)\n * and content differs; overwrite.\n *\n * The prompt is skipped entirely when `force` is true (non-interactive\n * overwrite) or when there is no existing file / content already matches.\n *\n * @internal Exported for unit testing only.\n */\nexport async function decideMcpConfigAction(options: {\n configPath: string | null;\n expectedContent: string;\n force: boolean;\n readFile?: (p: string) => string;\n existsSync?: (p: string) => boolean;\n prompt?: (question: string, defaultValue: boolean) => Promise<boolean>;\n}): Promise<\"write\" | \"skip\" | \"force-overwrite\"> {\n const { configPath, expectedContent, force } = options;\n if (configPath === null) return \"write\";\n\n const exists = options.existsSync ?? fs.existsSync;\n const read = options.readFile ?? ((p: string) => fs.readFileSync(p, \"utf-8\"));\n const prompt = options.prompt ?? promptYesNo;\n\n if (!exists(configPath)) return \"write\";\n\n let existingContent: string;\n try {\n existingContent = read(configPath);\n } catch {\n // Unreadable — treat as \"write\" since we can't assess drift.\n // This preserves the pre-hardening behavior for corrupt or\n // permission-restricted files.\n return \"write\";\n }\n\n if (mcpConfigMatches(existingContent, expectedContent)) {\n return \"write\";\n }\n\n if (force) {\n return \"force-overwrite\";\n }\n\n const answer = await prompt(\n `Existing MCP config at ${configPath} differs from Glasstrace's template. Overwrite?`,\n false,\n );\n return answer ? \"force-overwrite\" : \"skip\";\n}\n\n/**\n * Prompts the user with a yes/no question. Returns true for yes.\n * In non-interactive mode (no TTY), returns the default value.\n */\nasync function promptYesNo(question: string, defaultValue: boolean): Promise<boolean> {\n if (!process.stdin.isTTY) {\n return defaultValue;\n }\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise<boolean>((resolve) => {\n const suffix = defaultValue ? \" [Y/n] \" : \" [y/N] \";\n rl.question(question + suffix, (answer) => {\n rl.close();\n const trimmed = answer.trim().toLowerCase();\n if (trimmed === \"\") {\n resolve(defaultValue);\n return;\n }\n resolve(trimmed === \"y\" || trimmed === \"yes\");\n });\n });\n}\n\n/**\n * Identifies a scaffolding step that can be reversed during rollback.\n * Steps are tracked in execution order and rolled back in reverse.\n */\ntype CompletedStep =\n | \"instrumentation\"\n | \"next-config\"\n | \"env-local\"\n | \"gitignore\"\n | \"discovery-file\";\n\n/**\n * Tracks state needed for accurate rollback of init steps.\n * Separating this from the step list allows rollback to restore\n * original file content rather than doing surgical removal.\n */\ninterface RollbackState {\n steps: CompletedStep[];\n /**\n * Absolute path of the instrumentation file that the scaffolder\n * wrote to. May be either `{root}/instrumentation.ts` or\n * `{root}/src/instrumentation.ts` depending on the project layout\n * (DISC-493 Issue 1). When absent, rollback falls back to the\n * root path for backward compatibility with callers that do not\n * populate this field.\n */\n instrumentationPath?: string;\n /** Original instrumentation.ts content saved before injection.\n * When present, rollback restores this instead of using removeRegisterGlasstrace. */\n originalInstrumentationContent?: string;\n}\n\n/**\n * Removes leading blank lines that can appear after removing import lines.\n * Duplicated from uninit.ts to avoid exporting a trivial utility.\n */\nfunction cleanLeadingBlankLines(content: string): string {\n return content.replace(/^\\n{2,}/, \"\\n\");\n}\n\n/**\n * Returns true when the given `.gitignore` content would exclude the\n * static discovery file at `<root>/public/.well-known/glasstrace.json`\n * or `<root>/static/.well-known/glasstrace.json` (depending on `layout`)\n * from being committed.\n *\n * Model: the file has three ancestors in its committed path — the\n * static root directory (`public/` or `static/`), the `.well-known/`\n * sub-directory, and the file itself. Each pattern in `.gitignore` is\n * classified by which ancestor paths it matches, and each ancestor\n * carries its own current ignore state that later patterns can flip.\n * Patterns that don't match any of the three ancestors are ignored.\n *\n * Per `gitignore(5)`:\n *\n * > It is not possible to re-include a file if a parent directory of\n * > that file is excluded.\n *\n * Consequently, the file is reported as ignored when the final state\n * of ANY ancestor (static root, `.well-known/`, or the file itself)\n * is ignored — a `!<file>` negation alone cannot \"escape\" an ignored\n * ancestor.\n *\n * Per-ancestor tracking is what distinguishes overlapping patterns:\n *\n * - `public/` then `!public/` — root re-included, file OK.\n * - `public/` then `!public/.well-known/` — root still ignored (scope-2\n * negation doesn't match the scope-1 ancestor path), file ignored.\n * - `.well-known/` then `!public/.well-known/` — `.well-known/` re-included\n * (both patterns match the scope-2 ancestor), file OK.\n * - `.well-known/` then `!public/` — `!public/` matches the\n * scope-1 ancestor only; the scope-2 ancestor (`.well-known/`) is still\n * ignored, so the file is ignored.\n * - `<file>` then `!<file>` — file-level ignore flipped.\n * - `<file>` then `!public/.well-known/` — directory negation does\n * not match the file path; file still ignored.\n *\n * Not-modeled rules — glob wildcards (star, question mark, character\n * classes), overlapping any-depth matches across multiple parents, and\n * nested `.gitignore` files — skew the heuristic toward false positives\n * (extra warning output) rather than false negatives. The warning is\n * advisory.\n *\n * @internal Exported for unit testing only.\n */\nexport function gitignoreExcludesDiscoveryFile(\n gitignoreContent: string,\n layout: \"public\" | \"static\",\n): boolean {\n const staticRoot = layout === \"static\" ? \"static\" : \"public\";\n\n // Per-ancestor target sets. `matchesDiscoveryPath` handles trailing\n // slash and leading `**/` shapes on top of these exact paths.\n const rootTargets = [staticRoot, `${staticRoot}/`];\n const wellKnownTargets = [\n `${staticRoot}/.well-known`,\n `${staticRoot}/.well-known/`,\n \".well-known\",\n \".well-known/\",\n ];\n const fileTargets = [\n `${staticRoot}/.well-known/glasstrace.json`,\n \".well-known/glasstrace.json\",\n ];\n\n // Current ignore state of each ancestor path. Later patterns flip\n // these independently because a pattern only affects ancestors it\n // actually matches.\n let rootIgnored = false;\n let wellKnownIgnored = false;\n let fileIgnored = false;\n\n for (const rawLine of gitignoreContent.split(\"\\n\")) {\n const line = rawLine.trim();\n if (line === \"\" || line.startsWith(\"#\")) continue;\n\n const negation = line.startsWith(\"!\");\n const pattern = negation ? line.slice(1).trim() : line;\n if (pattern === \"\") continue;\n\n // Normalize: strip a leading slash (anchors to root in git's grammar;\n // for our purposes the patterns we check are all root-relative).\n const normalized = pattern.startsWith(\"/\") ? pattern.slice(1) : pattern;\n\n const matchesRoot = matchesDiscoveryPath(normalized, rootTargets);\n const matchesWellKnown = matchesDiscoveryPath(normalized, wellKnownTargets);\n const matchesFile = matchesDiscoveryPath(normalized, fileTargets);\n\n if (!matchesRoot && !matchesWellKnown && !matchesFile) continue;\n\n const newState = !negation;\n if (matchesRoot) rootIgnored = newState;\n if (matchesWellKnown) wellKnownIgnored = newState;\n if (matchesFile) fileIgnored = newState;\n }\n\n // A file is ignored whenever any ancestor's final state is ignored;\n // gitignore(5) does not permit re-including a file below an ignored\n // parent with a file-level `!<path>` pattern alone.\n return rootIgnored || wellKnownIgnored || fileIgnored;\n}\n\n/**\n * Returns true when the (normalized) gitignore pattern matches any of the\n * discovery-file-relevant paths. Supports three common pattern shapes:\n *\n * - Exact path: e.g. `public/.well-known/glasstrace.json`\n * - Directory: e.g. `.well-known/` or `public/.well-known`\n * - Leading `**\\/`: e.g. `**\\/.well-known/` (any-depth wildcard)\n *\n * Complex glob patterns (`*`, `?`, character classes) are not modeled;\n * the warning is advisory and false negatives are acceptable.\n */\nfunction matchesDiscoveryPath(\n pattern: string,\n targets: string[],\n): boolean {\n // Strip trailing slash — both `.well-known` and `.well-known/` should\n // match a directory-style target.\n const bare = pattern.endsWith(\"/\") ? pattern.slice(0, -1) : pattern;\n\n for (const target of targets) {\n const tBare = target.endsWith(\"/\") ? target.slice(0, -1) : target;\n if (bare === tBare) return true;\n // `**/X` matches any-depth occurrence of X\n if (pattern.startsWith(\"**/\")) {\n const suffix = pattern.slice(3);\n const sBare = suffix.endsWith(\"/\") ? suffix.slice(0, -1) : suffix;\n if (tBare === sBare || tBare.endsWith(`/${sBare}`)) return true;\n }\n }\n return false;\n}\n\n/**\n * Best-effort rollback of completed init steps in reverse order.\n * Each step is individually try/caught so that a failure in one\n * rollback does not prevent the remaining steps from being attempted.\n *\n * @internal Exported for unit testing only.\n */\nexport async function rollbackSteps(\n steps: CompletedStep[],\n projectRoot: string,\n state?: Omit<RollbackState, \"steps\">,\n): Promise<void> {\n for (const step of [...steps].reverse()) {\n try {\n switch (step) {\n case \"instrumentation\": {\n // Prefer the exact path the scaffolder wrote to — the resolver\n // may have chosen `src/instrumentation.ts` on Next.js `src/`\n // layouts (DISC-493 Issue 1). Fall back to the root path for\n // callers that do not populate `instrumentationPath`.\n const instrPath =\n state?.instrumentationPath ?? path.join(projectRoot, \"instrumentation.ts\");\n if (fs.existsSync(instrPath)) {\n const content = fs.readFileSync(instrPath, \"utf-8\");\n if (isInitCreatedInstrumentation(content)) {\n fs.unlinkSync(instrPath);\n } else if (state?.originalInstrumentationContent !== undefined) {\n // Restore the exact original content to avoid removing\n // pre-existing imports that removeRegisterGlasstrace would strip.\n fs.writeFileSync(instrPath, state.originalInstrumentationContent, \"utf-8\");\n } else {\n const cleaned = removeRegisterGlasstrace(content);\n if (cleaned !== content) {\n fs.writeFileSync(instrPath, cleaned, \"utf-8\");\n }\n }\n }\n break;\n }\n case \"next-config\": {\n for (const name of NEXT_CONFIG_NAMES) {\n const configPath = path.join(projectRoot, name);\n if (!fs.existsSync(configPath)) {\n continue;\n }\n const content = fs.readFileSync(configPath, \"utf-8\");\n if (!content.includes(\"withGlasstraceConfig\")) {\n continue;\n }\n const isESM = name.endsWith(\".ts\") || name.endsWith(\".mjs\");\n const unwrapResult = isESM\n ? unwrapExport(content)\n : unwrapCJSExport(content);\n if (unwrapResult.unwrapped) {\n const cleaned = removeGlasstraceConfigImport(unwrapResult.content);\n fs.writeFileSync(configPath, cleanLeadingBlankLines(cleaned), \"utf-8\");\n }\n break;\n }\n break;\n }\n case \"env-local\": {\n // Only remove GLASSTRACE_API_KEY lines — scaffoldEnvLocal (step 5)\n // only adds the API key. Removing GLASSTRACE_COVERAGE_MAP here would\n // delete a user's pre-existing coverage map setting if init fails\n // after step 5 but before the coverage map step.\n const envPath = path.join(projectRoot, \".env.local\");\n if (fs.existsSync(envPath)) {\n const content = fs.readFileSync(envPath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const filtered = lines.filter((line) => {\n const trimmed = line.trim();\n return !/^\\s*#?\\s*GLASSTRACE_API_KEY\\s*=/.test(trimmed);\n });\n if (filtered.length !== lines.length) {\n const result = filtered.join(\"\\n\");\n if (result.trim().length === 0) {\n fs.unlinkSync(envPath);\n } else {\n fs.writeFileSync(envPath, result, \"utf-8\");\n }\n }\n }\n break;\n }\n case \"gitignore\": {\n const gitignorePath = path.join(projectRoot, \".gitignore\");\n if (fs.existsSync(gitignorePath)) {\n const content = fs.readFileSync(gitignorePath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const filtered = lines.filter(\n (line) => line.trim() !== \".glasstrace/\",\n );\n if (filtered.length !== lines.length) {\n const result = filtered.join(\"\\n\");\n if (result.trim().length === 0) {\n fs.unlinkSync(gitignorePath);\n } else {\n fs.writeFileSync(gitignorePath, result, \"utf-8\");\n }\n }\n }\n break;\n }\n case \"discovery-file\": {\n // Remove the static discovery file scaffolded in Step 7. The\n // removeDiscoveryFile helper also best-effort removes an empty\n // `.well-known/` directory but never touches sibling content.\n removeDiscoveryFile(projectRoot);\n break;\n }\n }\n } catch {\n // Best-effort rollback — log nothing, continue with remaining steps\n }\n }\n}\n\n/**\n * Core init logic. Exported for testability — the CLI entry point at the\n * bottom calls this function and translates the result to process.exit().\n */\nexport async function runInit(options: InitOptions): Promise<InitResult> {\n const { yes, coverageMap } = options;\n const summary: string[] = [];\n const warnings: string[] = [];\n const errors: string[] = [];\n\n // Step 0: Resolve the correct project root (monorepo awareness)\n let projectRoot: string;\n try {\n const classification = resolveProjectRoot(options.projectRoot);\n projectRoot = classification.projectRoot;\n if (classification.isMonorepo && classification.appRelativePath) {\n summary.push(`Found Next.js app at ${classification.appRelativePath} — installing there`);\n }\n } catch (err) {\n errors.push(err instanceof Error ? err.message : String(err));\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 1: Detect package.json\n const packageJsonPath = path.join(projectRoot, \"package.json\");\n if (!fs.existsSync(packageJsonPath)) {\n errors.push(\"No package.json found. Run this command from a Node.js project root.\");\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Track completed steps so we can roll them back if a later step fails.\n // Only steps that modify the filesystem are tracked — pre-existing state\n // (e.g., \"already-registered\") is never rolled back.\n const rollbackState: RollbackState = { steps: [] };\n\n // Step 2: Ensure the instrumentation file has registerGlasstrace().\n // DISC-493 Issue 1: detect `src/` layout and merge into the existing file\n // rather than overwriting the project root.\n try {\n // Pre-resolve the target so we can save the original content (for\n // faithful rollback) and record the path for the rollback state.\n const preResolved = resolveInstrumentationTarget(projectRoot);\n if (!preResolved.conflict && preResolved.target !== null) {\n rollbackState.instrumentationPath = preResolved.target;\n if (fs.existsSync(preResolved.target)) {\n rollbackState.originalInstrumentationContent = fs.readFileSync(\n preResolved.target,\n \"utf-8\",\n );\n }\n }\n\n const instrResult = await scaffoldInstrumentation(projectRoot, {\n // `--yes` implies non-interactive automation and must not hang on a\n // merge confirmation prompt. `--force` skips the prompt explicitly\n // (DISC-1247 Scenario 2c parity).\n force: options.force === true || options.yes,\n });\n // Record the exact path the scaffolder wrote to, in case the resolver\n // and scaffolder ever disagree (symlinks, TOCTOU) — rollback is more\n // accurate when it targets the file that was actually mutated.\n if (instrResult.filePath !== undefined) {\n rollbackState.instrumentationPath = instrResult.filePath;\n }\n const relativePath =\n instrResult.filePath !== undefined\n ? path.relative(projectRoot, instrResult.filePath)\n : \"instrumentation.ts\";\n switch (instrResult.action) {\n case \"created\":\n summary.push(`Created ${relativePath}`);\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"injected\":\n summary.push(`Added registerGlasstrace() to existing ${relativePath}`);\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"appended\":\n summary.push(\n `Appended register() with registerGlasstrace() to ${relativePath}`,\n );\n rollbackState.steps.push(\"instrumentation\");\n break;\n case \"already-registered\":\n summary.push(`Skipped ${relativePath} (registerGlasstrace already present)`);\n break;\n case \"skipped\":\n // User declined the merge prompt (DISC-1247 Scenario 2c parity).\n // Emit a warning so re-init output makes clear nothing changed.\n // \"--force\" here only bypasses the confirmation — the scaffolder\n // merges rather than overwriting, so the wording is deliberate.\n warnings.push(\n `Preserved ${relativePath} (merge declined; re-run with --force to apply the merge without prompting)`,\n );\n break;\n case \"conflict\": {\n // Both root and src/ instrumentation files exist — Next.js's\n // loader behavior is undefined (DISC-493 Issue 1). Refuse to\n // write a third competing file; point the user at the file to\n // merge into and tell them to remove the other.\n const primary =\n instrResult.filePath !== undefined\n ? path.relative(projectRoot, instrResult.filePath)\n : \"src/instrumentation.ts\";\n const competing =\n instrResult.conflictingPath !== undefined\n ? path.relative(projectRoot, instrResult.conflictingPath)\n : \"instrumentation.ts\";\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(\n `Both ${primary} and ${competing} exist. Next.js's loader behavior is undefined when both are present.\\n` +\n `Merge your instrumentation into ${primary} and remove ${competing}, then re-run init.`,\n );\n return { exitCode: 1, summary, warnings, errors };\n }\n case \"unrecognized\":\n warnings.push(\n `${relativePath} exists but has no recognizable register() function.\\n` +\n \"Add this import at the top of your file:\\n\\n\" +\n ' import { registerGlasstrace } from \"@glasstrace/sdk\";\\n\\n' +\n \"Then add this as the first statement in your register() function:\\n\\n\" +\n \" registerGlasstrace();\\n\",\n );\n break;\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to write instrumentation file: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 4: Detect and wrap next.config.*\n try {\n const configResult = await scaffoldNextConfig(projectRoot);\n if (configResult?.modified) {\n summary.push(\"Wrapped next.config with withGlasstraceConfig()\");\n rollbackState.steps.push(\"next-config\");\n } else if (configResult === null) {\n warnings.push(\"No next.config.* found. You may need to create one for Next.js projects.\");\n } else if (configResult.reason === \"already-wrapped\") {\n summary.push(\"Skipped next.config (already contains withGlasstraceConfig)\");\n } else if (configResult.reason === \"empty-file\") {\n warnings.push(\"next.config is empty — add a Next.js configuration export to enable wrapping\");\n } else {\n warnings.push(\"next.config has no recognizable export pattern — add withGlasstraceConfig() manually\");\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to modify next.config: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 5: Update .env.local\n // DISC-1247 Scenario 6: if .env.local already defines a claimed\n // developer key (gt_dev_*), scaffoldEnvLocal preserves it and this\n // step reports the preservation so the user knows re-init did not\n // overwrite their claim.\n try {\n const envPathForCheck = path.join(projectRoot, \".env.local\");\n let existingDevKey = false;\n if (fs.existsSync(envPathForCheck)) {\n const existingContent = fs.readFileSync(envPathForCheck, \"utf-8\");\n existingDevKey = isDevApiKey(readEnvLocalApiKey(existingContent));\n }\n const envCreated = await scaffoldEnvLocal(projectRoot);\n if (envCreated) {\n summary.push(\"Updated .env.local with Glasstrace configuration\");\n rollbackState.steps.push(\"env-local\");\n } else if (existingDevKey) {\n summary.push(\n \"Preserved existing .env.local (GLASSTRACE_API_KEY contains a claimed dev key)\",\n );\n } else {\n summary.push(\"Skipped .env.local (GLASSTRACE_API_KEY already configured)\");\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to update .env.local: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 6: Update .gitignore\n try {\n const gitignoreUpdated = await scaffoldGitignore(projectRoot);\n if (gitignoreUpdated) {\n summary.push(\"Updated .gitignore with .glasstrace/\");\n rollbackState.steps.push(\"gitignore\");\n } else {\n summary.push(\"Skipped .gitignore (.glasstrace/ already listed)\");\n }\n } catch (err) {\n await rollbackSteps(rollbackState.steps, projectRoot, rollbackState);\n errors.push(`Failed to update .gitignore: ${err instanceof Error ? err.message : String(err)}`);\n return { exitCode: 1, summary, warnings, errors };\n }\n\n // Step 7: MCP auto-configuration\n // Use CI env vars (not TTY check) to distinguish automated builds from\n // manual CLI usage. TTY state is unreliable — piped output, test runners,\n // and IDE terminals all report isTTY=false despite being user-initiated.\n // Accept any truthy CI value (GitHub Actions, GitLab, CircleCI, Travis,\n // etc.) and also check GITHUB_ACTIONS specifically.\n const ciEnv = process.env[\"CI\"];\n const isCI =\n (typeof ciEnv === \"string\" &&\n ciEnv.trim() !== \"\" &&\n ciEnv.toLowerCase() !== \"false\" &&\n ciEnv.trim() !== \"0\") ||\n process.env[\"GITHUB_ACTIONS\"] === \"true\";\n\n try {\n // DISC-1247 Scenario 2a: preserve any existing anonymous key.\n // getOrCreateAnonKey already reads an existing key if present, so\n // re-running init never overwrites a key that may be linked to an\n // account. We explicitly check first so we can report the preservation\n // in the summary — without this, users have no feedback that re-init\n // respected their existing claim linkage.\n const preExistingAnonKey = await readAnonKey(projectRoot);\n const anonKey = await getOrCreateAnonKey(projectRoot);\n if (preExistingAnonKey !== null) {\n summary.push(\"Preserved existing .glasstrace/anon_key\");\n }\n\n // Step 7a: Write the static discovery file at\n // `<staticRoot>/.well-known/glasstrace.json` so the Glasstrace browser\n // extension can discover the project's anon key without a runtime HTTP\n // handler (design doc \"SDK Discovery Endpoint / Static File\" §6.1).\n //\n // Re-init (DISC-1247 Scenario 2) preserves any user-added fields and\n // only rewrites when the on-disk key no longer matches. A failed\n // write is surfaced as a warning rather than an init-blocking error:\n // scaffolding has already succeeded at this point and the user can\n // unblock themselves by inspecting the path and re-running.\n try {\n const discoveryResult = writeDiscoveryFile(projectRoot, anonKey);\n const relPath = relativeDiscoveryPath(discoveryResult.layout);\n switch (discoveryResult.action) {\n case \"created\":\n summary.push(`Created ${relPath}`);\n rollbackState.steps.push(\"discovery-file\");\n break;\n case \"updated-stale\":\n summary.push(`Updated ${relPath} (anon key had changed)`);\n // Not pushed to rollback: the pre-existing file was user-owned\n // content (just with a stale key). A rollback should restore\n // the original key rather than delete the file outright, and\n // we do not snapshot the prior content. Leaving it alone is\n // the safer behavior.\n break;\n case \"skipped-matches\":\n summary.push(`Skipped ${relPath} (already matches anon key)`);\n break;\n case \"skipped-foreign\":\n summary.push(\n `Rewrote ${relPath} (existing file was malformed or not SDK-managed)`,\n );\n // Same reasoning as \"updated-stale\": we did not snapshot the\n // original content, so rollback can only delete. Leaving off\n // the rollback list prevents data loss on a later-step failure.\n break;\n case \"failed\":\n warnings.push(\n `Failed to write ${relPath}${\n discoveryResult.error !== undefined\n ? `: ${discoveryResult.error}`\n : \"\"\n }. The Glasstrace browser extension will fall back to the runtime handler until the file is written.`,\n );\n break;\n }\n\n // Emit a warning if the user's `.gitignore` excludes `.well-known/`\n // or the discovery file path — otherwise the file will not be\n // deployed and the extension will silently fail to discover the\n // project. We never modify `.gitignore` (the file is public\n // metadata, not a secret).\n const gitignorePath = path.join(projectRoot, \".gitignore\");\n if (fs.existsSync(gitignorePath)) {\n try {\n const gitignoreContent = fs.readFileSync(gitignorePath, \"utf-8\");\n if (gitignoreExcludesDiscoveryFile(gitignoreContent, discoveryResult.layout)) {\n warnings.push(\n `Your .gitignore excludes ${relPath} (directly or via a parent rule). ` +\n \"The discovery file must be committed for the Glasstrace browser extension to find it in deployed builds. \" +\n \"Remove the matching line from .gitignore or add an explicit negation (e.g. `!\" +\n relPath +\n \"`).\",\n );\n }\n } catch {\n // Unreadable .gitignore is not actionable here — skip the check.\n }\n }\n } catch (err) {\n warnings.push(\n `Failed to write ${relativeDiscoveryPath(\n resolveStaticRoot(projectRoot).layout,\n )}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n // `anyConfigWritten` covers both fresh writes AND user-preserved\n // configs — it gates nudge suppression (we don't want to pester\n // users about MCP setup they consciously preserved) and the\n // gitignore update.\n //\n // `anyConfigRewrittenWithBearer` is stricter: it tracks whether\n // this invocation actually wrote new content with the resolved\n // bearer. The marker is gated on this stricter flag so we don't\n // stamp the marker as \"this credential is what's on disk\" when the\n // user declined an overwrite — that would let a later\n // `glasstrace mcp add` falsely conclude the project is already\n // configured for the new credential and skip the refresh\n // (DISC-1512).\n let anyConfigWritten = false;\n let anyConfigRewrittenWithBearer = false;\n\n // Resolve the effective MCP credential. Managed configs embed\n // whichever bearer the project is authenticating ingestion with —\n // anon key on first init, dev key on a re-init after an account\n // claim. The discovery file at `.well-known/glasstrace.json`\n // continues to use the anon key (it is a public project\n // identifier read by the browser extension, not an auth token).\n const resolved = await resolveEffectiveMcpCredential(projectRoot);\n const bearer = resolved.effective?.key ?? anonKey;\n\n if (isCI) {\n // Non-interactive: write only the generic .glasstrace/mcp.json.\n // CI uses `force: true` for MCP diff decisions because there's no\n // interactive terminal to prompt on — existing configs in CI\n // workspaces are rare and safe to overwrite.\n const genericAgent: DetectedAgent = {\n name: \"generic\",\n mcpConfigPath: path.join(projectRoot, \".glasstrace\", \"mcp.json\"),\n infoFilePath: null,\n cliAvailable: false,\n registrationCommand: null,\n };\n const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, bearer);\n const decision = await decideMcpConfigAction({\n configPath: genericAgent.mcpConfigPath,\n expectedContent: genericConfig,\n force: true,\n });\n if (decision !== \"skip\") {\n await writeMcpConfig(genericAgent, genericConfig, projectRoot);\n if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigRewrittenWithBearer = true;\n }\n }\n if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigWritten = true;\n summary.push(\"Created .glasstrace/mcp.json (CI mode)\");\n }\n } else {\n // Interactive: detect agents and configure each\n let agents: DetectedAgent[];\n try {\n agents = await detectAgents(projectRoot);\n } catch (detectErr) {\n warnings.push(\n `Agent detection failed: ${detectErr instanceof Error ? detectErr.message : String(detectErr)}. Writing generic config only.`,\n );\n // Fall back to generic-only config\n const genericAgent: DetectedAgent = {\n name: \"generic\",\n mcpConfigPath: path.join(projectRoot, \".glasstrace\", \"mcp.json\"),\n infoFilePath: null,\n cliAvailable: false,\n registrationCommand: null,\n };\n const genericConfig = generateMcpConfig(genericAgent, MCP_ENDPOINT, bearer);\n await writeMcpConfig(genericAgent, genericConfig, projectRoot);\n if (genericAgent.mcpConfigPath !== null && fs.existsSync(genericAgent.mcpConfigPath)) {\n anyConfigWritten = true;\n anyConfigRewrittenWithBearer = true;\n }\n agents = [];\n }\n\n const configuredNames: string[] = [];\n\n for (const agent of agents) {\n try {\n const configContent = generateMcpConfig(agent, MCP_ENDPOINT, bearer);\n\n // Diff-aware MCP write (DISC-1247 Scenario 2c): if the existing\n // config differs from what init would write, prompt before\n // overwriting. `--force` (or --yes in non-interactive mode)\n // skips the prompt.\n const decision = await decideMcpConfigAction({\n configPath: agent.mcpConfigPath,\n expectedContent: configContent,\n force: options.force === true || options.yes,\n });\n\n if (decision === \"skip\") {\n summary.push(\n `Preserved existing ${agent.mcpConfigPath ?? agent.name} (user declined overwrite)`,\n );\n if (agent.mcpConfigPath !== null && fs.existsSync(agent.mcpConfigPath)) {\n // Count existing user-edited config as \"present\" so the\n // marker file still gets written — otherwise nudges would\n // nag the user about MCP setup they consciously preserved.\n anyConfigWritten = true;\n }\n continue;\n }\n\n await writeMcpConfig(agent, configContent, projectRoot);\n\n // Verify the config file was actually written (writeMcpConfig\n // swallows permission errors and returns void)\n const configExists = agent.mcpConfigPath !== null && fs.existsSync(agent.mcpConfigPath);\n if (!configExists) {\n continue;\n }\n\n anyConfigWritten = true;\n anyConfigRewrittenWithBearer = true;\n\n const infoContent = generateInfoSection(agent, MCP_ENDPOINT);\n if (infoContent !== \"\") {\n await injectInfoSection(agent, infoContent, projectRoot);\n }\n\n if (agent.name !== \"generic\") {\n configuredNames.push(formatAgentName(agent.name));\n }\n } catch (agentErr) {\n warnings.push(\n `Failed to configure MCP for ${agent.name}: ${agentErr instanceof Error ? agentErr.message : String(agentErr)}`,\n );\n }\n }\n\n if (configuredNames.length > 0) {\n summary.push(`Configured MCP for: ${configuredNames.join(\", \")}`);\n } else if (anyConfigWritten) {\n summary.push(\"Created .glasstrace/mcp.json (generic config)\");\n }\n }\n\n // Add MCP config files to .gitignore\n await updateGitignore(\n [\".mcp.json\", \".cursor/mcp.json\", \".gemini/settings.json\", \".codex/config.toml\"],\n projectRoot,\n );\n\n // Create marker file only if this invocation actually wrote a\n // config with the resolved bearer. We intentionally use the\n // stricter `anyConfigRewrittenWithBearer` flag instead of the\n // looser `anyConfigWritten`: stamping the marker for a credential\n // we did not actually write to disk would let a later\n // `glasstrace mcp add` short-circuit on a stale config, missing\n // the claim-transition refresh (DISC-1512). When all configs were\n // user-preserved, no marker is written — the existing on-disk\n // marker (if any) stays untouched so its source/hash continues to\n // describe what was actually written previously.\n if (anyConfigRewrittenWithBearer) {\n const markerSource = resolved.effective?.source ?? \"anon\";\n const markerHash = identityFingerprint(bearer);\n const markerCreated = await writeMcpMarker(projectRoot, {\n credentialSource: markerSource,\n credentialHash: markerHash,\n });\n if (markerCreated) {\n summary.push(\"Created .glasstrace/mcp-connected marker\");\n }\n }\n } catch (mcpErr) {\n warnings.push(\n `MCP auto-configuration failed: ${mcpErr instanceof Error ? mcpErr.message : String(mcpErr)}`,\n );\n }\n\n // Step 8: Coverage map opt-in\n let enableCoverageMap = coverageMap;\n if (!yes && !coverageMap) {\n if (process.stdin.isTTY) {\n enableCoverageMap = await promptYesNo(\n \"Would you like to enable test coverage mapping?\",\n false,\n );\n }\n }\n\n if (enableCoverageMap) {\n try {\n const added = await addCoverageMapEnv(projectRoot);\n if (added) {\n summary.push(\"Added GLASSTRACE_COVERAGE_MAP=true to .env.local\");\n }\n } catch (err) {\n warnings.push(`Failed to add coverage map env: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n // Step 9: Run initial import graph scan\n try {\n await buildImportGraph(projectRoot);\n summary.push(\"Completed initial import graph scan\");\n } catch (err) {\n warnings.push(`Import graph scan failed: ${err instanceof Error ? err.message : String(err)}. You can run it later.`);\n }\n }\n\n // Step 10: Blocking verification that the anon key is registered\n // server-side (DISC-493 Issue 3, DISC-494). Runs last so failures here\n // surface the exit code without blocking the file scaffolding work.\n //\n // Skipped when:\n // - `GLASSTRACE_SKIP_INIT_VERIFY=1` is set (offline installs)\n // - CI mode is active (CI builds should not reach external networks)\n // - Vitest is running (`VITEST` env var) — unit tests that don't\n // care about the network path shouldn't be forced to mock the\n // transport; tests that DO care use `_setTransportForTesting`\n // and explicitly un-set `VITEST` before calling runInit.\n const skipVerify =\n process.env[\"GLASSTRACE_SKIP_INIT_VERIFY\"] === \"1\" ||\n process.env[\"GLASSTRACE_SKIP_INIT_VERIFY\"] === \"true\" ||\n process.env[\"VITEST\"] === \"true\";\n\n if (!skipVerify && !isCI) {\n const verifyResult = await verifyAnonKeyRegistration(projectRoot);\n if (verifyResult.outcome === \"failed\") {\n errors.push(verifyResult.error);\n return { exitCode: 2, summary, warnings, errors };\n }\n if (verifyResult.outcome === \"verified\") {\n summary.push(\"Verified anon key registration with Glasstrace API\");\n } else {\n // \"skipped\": no anon key on disk means MCP auto-configuration\n // failed earlier (a warning was already emitted). Reporting\n // \"Verified ...\" here would misrepresent the state of setup —\n // a verification request was never even sent.\n summary.push(\"Skipped anon key verification (no anon key on disk)\");\n }\n }\n\n return { exitCode: 0, summary, warnings, errors };\n}\n\n/**\n * Outcome of {@link verifyAnonKeyRegistration}:\n *\n * - `\"verified\"` — the server registered the anon key (HTTP 2xx with a\n * schema-valid response body).\n * - `\"skipped\"` — no anon key was on disk, so no verification request\n * was sent. Typically means MCP auto-configuration failed earlier\n * (warnings were already emitted) — distinct from a verification\n * success.\n * - `\"failed\"` — the verification request was sent and failed.\n * `error` is a user-facing message distinguishing the failure class\n * (`fetch failed: ...`, `server rejected the key ...`, or\n * `server returned malformed response ...`) with no anon key bytes.\n */\nexport type VerifyAnonKeyOutcome =\n | { outcome: \"verified\" }\n | { outcome: \"skipped\" }\n | { outcome: \"failed\"; error: string };\n\n/**\n * Verifies that the anonymous key written by init is registered\n * server-side. Called at the end of the scaffold flow so that when it\n * fails, the user sees an actionable error rather than a misleading\n * \"initialized successfully\" followed by silent MCP authentication\n * failures (DISC-494).\n *\n * Returns a discriminated outcome so the CLI can distinguish a genuine\n * verification pass from the \"no anon key on disk\" skip case. On\n * failure the error message distinguishes three classes:\n * - \"fetch failed\" — transport error (DNS, TCP, TLS, timeout)\n * - \"server rejected the key\" — HTTP 4xx/5xx\n * - \"server returned malformed response\" — HTTP 2xx with unparseable\n * body\n *\n * The anon key is NEVER included in the returned message. Callers\n * (the CLI entry point) render it verbatim to stderr via the errors\n * array and exit non-zero.\n *\n * @internal Exported for testability.\n */\nexport async function verifyAnonKeyRegistration(\n projectRoot: string,\n): Promise<VerifyAnonKeyOutcome> {\n // Read env the same way the runtime would — scaffoldEnvLocal may have\n // written GLASSTRACE_API_KEY=gt_anon_..., so prefer the generated\n // anon key on disk as the authoritative value to verify.\n const anonKey = await readAnonKey(projectRoot);\n if (anonKey === null) {\n // No anon key on disk means MCP wasn't configured. Skip\n // verification — this is a partial install, not a verification\n // failure. The init flow already emitted warnings above. Report\n // \"skipped\" so the caller doesn't claim verification succeeded.\n return { outcome: \"skipped\" };\n }\n\n // Load the dev key from .env.local if present (for straggler linking)\n // but fall through to anon-only verification otherwise. Reading is\n // best-effort: any error leaves devKey undefined and we verify the\n // anon key in isolation.\n //\n // Use `readEnvLocalApiKey` (shared with the rest of the init flow) so\n // we match dotenv-style last-wins semantics. A hand-rolled regex here\n // would pick the FIRST `GLASSTRACE_API_KEY=` assignment, which in a\n // rotated env file would authenticate with a stale key and surface a\n // false `server rejected` failure even when the effective key is\n // valid.\n let devKey: string | undefined;\n try {\n const envPath = path.join(projectRoot, \".env.local\");\n if (fs.existsSync(envPath)) {\n const envContent = fs.readFileSync(envPath, \"utf-8\");\n const effective = readEnvLocalApiKey(envContent);\n // Only send a real dev key in the dev slot — don't double-count\n // the anon key as both keys.\n if (effective !== null && isDevApiKey(effective)) {\n devKey = effective;\n }\n }\n } catch {\n // Ignore — fall through to anon-only verification.\n }\n\n // Resolve endpoint / environment from the user's shell, then pin\n // `apiKey` to whatever we read from .env.local. `resolveConfig` uses\n // `??` to fall back to `process.env.GLASSTRACE_API_KEY` when the\n // option is undefined, which would cause verification to authenticate\n // with an unrelated stale key in shells that export\n // `GLASSTRACE_API_KEY` — producing false \"server rejected\" failures\n // even when the freshly generated anon key is valid. The explicit\n // override keeps verification tied to disk state.\n const baseConfig = resolveConfig({ apiKey: devKey });\n const config = { ...baseConfig, apiKey: devKey };\n const sdkVersion = typeof __SDK_VERSION__ === \"string\" ? __SDK_VERSION__ : \"0.0.0-dev\";\n\n const result: VerifyInitResult = await verifyInitReachable(config, anonKey, sdkVersion);\n\n if (result.ok) {\n return { outcome: \"verified\" };\n }\n\n const hint = \"Run 'npx glasstrace status' or 'npx glasstrace doctor' to diagnose.\";\n switch (result.reason) {\n case \"transport\":\n // `result.detail` is the raw cause with any leading `fetch failed: `\n // stripped by verifyInitReachable. Format once here so the output\n // matches the documented `fetch failed: <reason>` shape and never\n // renders `fetch failed (fetch failed: ...)`.\n return {\n outcome: \"failed\",\n error: `Glasstrace init verification failed: fetch failed: ${result.detail}. ${hint}`,\n };\n case \"rejected\":\n return {\n outcome: \"failed\",\n error: `Glasstrace init verification failed: server rejected the key (HTTP ${result.status}). ${hint}`,\n };\n case \"malformed\":\n return {\n outcome: \"failed\",\n error: `Glasstrace init verification failed: server returned malformed response. ${hint}`,\n };\n }\n}\n\n/**\n * Parses CLI arguments into InitOptions.\n */\nfunction parseArgs(argv: string[]): InitOptions {\n const args = argv.slice(2); // skip node + script path\n let yes = false;\n let coverageMap = false;\n let force = false;\n\n for (const arg of args) {\n if (arg === \"--yes\" || arg === \"-y\") {\n yes = true;\n } else if (arg === \"--coverage-map\") {\n coverageMap = true;\n } else if (arg === \"--force\") {\n force = true;\n }\n }\n\n // Auto-detect non-interactive\n if (!process.stdin.isTTY) {\n yes = true;\n }\n\n return {\n projectRoot: process.cwd(),\n yes,\n coverageMap,\n force,\n };\n}\n\n/**\n * CLI entry point. Only runs when this module is executed directly\n * (not when imported for testing).\n */\nconst scriptPath =\n typeof process !== \"undefined\" && process.argv[1] !== undefined\n ? process.argv[1].replace(/\\\\/g, \"/\")\n : undefined;\n\nconst scriptBasename = scriptPath !== undefined ? path.basename(scriptPath) : undefined;\n\nconst isDirectExecution =\n scriptPath !== undefined &&\n (scriptPath.endsWith(\"/cli/init.js\") ||\n scriptPath.endsWith(\"/cli/init.ts\") ||\n scriptBasename === \"glasstrace\");\n\nif (isDirectExecution) {\n // Enforce minimum Node.js version before any command processing.\n // The engines field in package.json is advisory — npm does not enforce\n // it by default, so this provides a clear error for users on older runtimes.\n if (!meetsNodeVersion(20)) {\n process.stderr.write(\n `Error: @glasstrace/sdk requires Node.js >= 20. Current version: ${process.version}\\n`,\n );\n process.exit(1);\n }\n\n const subcommand = process.argv[2];\n\n if (subcommand === \"mcp\") {\n if (process.argv[3] === \"add\") {\n // Parse --force and --dry-run from remaining args\n const remainingArgs = process.argv.slice(4);\n const force = remainingArgs.includes(\"--force\");\n const dryRun = remainingArgs.includes(\"--dry-run\");\n\n import(\"./mcp-add.js\")\n .then(({ mcpAdd }) => mcpAdd({ force, dryRun }))\n .then((result) => {\n for (const msg of result.messages) {\n process.stderr.write(msg + \"\\n\");\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else {\n process.stderr.write(\n `Unknown mcp subcommand: ${process.argv[3] ?? \"(none)\"}\\n\\n` +\n \"Usage: glasstrace mcp add [--force] [--dry-run]\\n\",\n );\n process.exit(1);\n }\n } else if (subcommand === undefined || subcommand === \"init\" || subcommand.startsWith(\"-\")) {\n // Default: run init (handles `glasstrace`, `glasstrace init`, `glasstrace --yes`)\n const forwardedArgs = process.argv.slice(subcommand === \"init\" ? 3 : 2);\n\n // `--validate` is an init sub-mode that checks artifact consistency\n // without scaffolding (DISC-1247 Scenario 4). We dispatch to a\n // dedicated module so the main init path stays unburdened.\n //\n // Resolve the app root via the same monorepo-aware logic that\n // `runInit` and `runStatus` use so validation in a monorepo root\n // inspects the actual Next.js app directory rather than the empty\n // workspace root (addresses Codex P2 review feedback).\n if (forwardedArgs.includes(\"--validate\")) {\n let validateProjectRoot = process.cwd();\n try {\n validateProjectRoot = resolveProjectRoot(validateProjectRoot).projectRoot;\n } catch {\n // Fall back to cwd if the monorepo resolver can't find an app —\n // validate can still report orphan-artifact issues at the raw\n // cwd and will exit non-zero rather than hiding the problem.\n }\n import(\"./validate.js\")\n .then(({ runValidate }) => runValidate({ projectRoot: validateProjectRoot }))\n .then((result) => {\n for (const line of result.summary) {\n process.stderr.write(`${line}\\n`);\n }\n for (const issue of result.issues) {\n process.stderr.write(` - ${issue.message}\\n`);\n if (issue.fix) {\n process.stderr.write(` Fix: ${issue.fix}\\n`);\n }\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else {\n const options = parseArgs(process.argv);\n\n runInit(options)\n .then((result) => {\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n process.stderr.write(`Error: ${err}\\n`);\n }\n }\n if (result.warnings.length > 0) {\n for (const warn of result.warnings) {\n process.stderr.write(`Warning: ${warn}\\n`);\n }\n }\n if (result.summary.length > 0) {\n // Only claim success when exitCode is 0. A non-zero exit\n // means init scaffolding completed but a later step (e.g.,\n // verifyAnonKeyRegistration) failed — we must not tell the\n // user everything is fine (DISC-494 root cause).\n if (result.exitCode === 0) {\n process.stderr.write(\"\\nGlasstrace initialized successfully!\\n\\n\");\n } else {\n process.stderr.write(\"\\nGlasstrace init completed with errors.\\n\\n\");\n }\n for (const line of result.summary) {\n process.stderr.write(` - ${line}\\n`);\n }\n if (result.exitCode === 0) {\n process.stderr.write(\"\\nNext steps:\\n\");\n process.stderr.write(\" 1. Start your Next.js dev server\\n\");\n process.stderr.write(\n \" 2. Glasstrace works immediately in anonymous mode\\n\",\n );\n process.stderr.write(\n \" 3. To link to your account, set GLASSTRACE_API_KEY in .env.local\\n\\n\",\n );\n }\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n }\n } else if (subcommand === \"uninit\") {\n const remainingArgs = process.argv.slice(3);\n const dryRun = remainingArgs.includes(\"--dry-run\");\n const force = remainingArgs.includes(\"--force\");\n\n import(\"./uninit.js\")\n .then(({ runUninit }) => runUninit({ projectRoot: process.cwd(), dryRun, force }))\n .then((result) => {\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n process.stderr.write(`Error: ${err}\\n`);\n }\n }\n if (result.warnings.length > 0) {\n for (const warn of result.warnings) {\n process.stderr.write(`Warning: ${warn}\\n`);\n }\n }\n if (result.summary.length > 0) {\n process.stderr.write(\"\\n\");\n for (const line of result.summary) {\n process.stderr.write(` ${line}\\n`);\n }\n process.stderr.write(\"\\n\");\n }\n process.exit(result.exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else if (subcommand === \"status\") {\n const remainingArgs = process.argv.slice(3);\n const json = remainingArgs.includes(\"--json\");\n\n Promise.all([import(\"./status.js\"), import(\"./monorepo.js\")])\n .then(([{ runStatus }, { resolveProjectRoot: resolve }]) => {\n let projectRoot = process.cwd();\n try {\n projectRoot = resolve(projectRoot).projectRoot;\n } catch {\n // Fall back to cwd if monorepo resolution fails\n }\n const result = runStatus({ projectRoot });\n if (json) {\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n } else {\n const checks = [\n [\"Installed\", result.installed],\n [\"Initialized\", result.initialized],\n [\"Instrumentation\", result.instrumentation],\n [\"Config wrapped\", result.configWrapped],\n [\"Anon key\", result.anonKey],\n [\"MCP configured\", result.mcpConfigured],\n ] as const;\n for (const [label, ok] of checks) {\n process.stderr.write(` ${ok ? \"+\" : \"-\"} ${label}\\n`);\n }\n if (result.agents.length > 0) {\n process.stderr.write(` + Agents: ${result.agents.join(\", \")}\\n`);\n } else {\n process.stderr.write(\" - Agents\\n\");\n }\n }\n process.exit(0);\n })\n .catch((err: unknown) => {\n process.stderr.write(\n `Fatal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n });\n } else {\n process.stderr.write(\n `Unknown command: ${subcommand}\\n\\n` +\n \"Usage:\\n\" +\n \" glasstrace init [--yes] [--coverage-map] [--force] [--validate]\\n\" +\n \" glasstrace uninit [--dry-run] [--force]\\n\" +\n \" glasstrace status [--json]\\n\" +\n \" glasstrace mcp add [--force] [--dry-run]\\n\",\n );\n process.exit(1);\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { NEXT_CONFIG_NAMES } from \"./constants.js\";\nimport { identityFingerprint, writeMcpMarker } from \"../mcp-runtime.js\";\n\n/**\n * Checks whether `content` contains a real (non-commented) `registerGlasstrace()` call.\n *\n * Strips single-line `// ...` comments before matching so that\n * `// registerGlasstrace()` is not treated as a real invocation.\n * Block comments are not stripped — block-commenting a function call\n * while keeping it syntactically valid is extremely unlikely in practice.\n *\n * @internal Exported for unit testing only.\n */\nexport function hasRegisterGlasstraceCall(content: string): boolean {\n return content.split(\"\\n\").some((line) => {\n const uncommented = line.replace(/\\/\\/.*$/, \"\");\n return /\\bregisterGlasstrace\\s*\\(/.test(uncommented);\n });\n}\n\n/** Result of attempting to inject registerGlasstrace into an existing file. */\nexport interface InjectResult {\n content: string;\n injected: boolean;\n}\n\n/** Result of the instrumentation.ts scaffolding step. */\nexport type InstrumentationAction =\n | \"created\"\n | \"injected\"\n | \"appended\"\n | \"already-registered\"\n | \"skipped\"\n | \"unrecognized\"\n | \"conflict\";\n\n/**\n * Layout detected by {@link resolveInstrumentationTarget}. `root` means\n * the project has no `src/` directory so instrumentation lives at the\n * project root; `src` means Next.js expects `src/instrumentation.ts`.\n */\nexport type InstrumentationLayout = \"root\" | \"src\";\n\n/** Structured result from scaffoldInstrumentation. */\nexport interface ScaffoldInstrumentationResult {\n action: InstrumentationAction;\n /**\n * The instrumentation file path this scaffold step targeted. Absolute\n * path (e.g., `/abs/project/instrumentation.ts` or\n * `/abs/project/src/instrumentation.ts`). Present for every successful\n * action so callers (init.ts summary lines, rollback state) can report\n * and unwind without re-detecting the layout.\n *\n * For `\"conflict\"`, this is the recommended merge target — the `src/`\n * variant when both exist, because that's where Next.js loads from on\n * modern `src/`-layout projects. The competing path is available via\n * {@link ScaffoldInstrumentationResult.conflictingPath}.\n */\n filePath?: string;\n /** Layout the resolver chose. Always set for non-conflict actions. */\n layout?: InstrumentationLayout;\n /**\n * When `action === \"conflict\"`, the path of the other instrumentation\n * file whose presence Next.js treats as undefined. The recommended merge\n * target is {@link ScaffoldInstrumentationResult.filePath}.\n */\n conflictingPath?: string;\n}\n\n/** Options for {@link scaffoldInstrumentation}. */\nexport interface ScaffoldInstrumentationOptions {\n /**\n * When `true`, skip the merge-confirmation prompt and write changes\n * without asking. Matches the DISC-1247 Scenario 2c `--force` behavior\n * already used for MCP config overwrites. Defaults to `false`.\n */\n force?: boolean;\n /**\n * Interactive prompt callback. When omitted, `scaffoldInstrumentation`\n * uses a TTY readline prompt and defaults to `false` (skip the change)\n * in non-interactive shells — the same pattern `decideMcpConfigAction`\n * follows. Exposed for testing.\n */\n prompt?: (question: string, defaultValue: boolean) => Promise<boolean>;\n}\n\n/** Result of attempting to wrap next.config with withGlasstraceConfig. */\nexport interface ScaffoldNextConfigResult {\n modified: boolean;\n reason?: \"already-wrapped\" | \"empty-file\" | \"no-export\";\n}\n\n/**\n * Injects `registerGlasstrace()` into an existing instrumentation.ts file.\n *\n * Strategy:\n * 1. If the file already contains a real `registerGlasstrace()` call — no-op\n * (commented-out calls are ignored)\n * 2. Find `export [async] function register()` pattern\n * 3. Add `import { registerGlasstrace } from \"@glasstrace/sdk\"` at top\n * (or extend existing `@glasstrace/sdk` import, skipping if already imported)\n * 4. Insert `registerGlasstrace()` as the first statement in the function body\n *\n * @param content - The existing file content\n * @returns The modified content if injection succeeded, or the original content\n * with `injected: false` if the pattern was not recognized\n */\nexport function injectRegisterGlasstrace(content: string): InjectResult {\n // Already has a registerGlasstrace() call — no-op.\n // Uses a helper that strips single-line comments before matching\n // so that `// registerGlasstrace()` is not treated as a real call.\n if (hasRegisterGlasstraceCall(content)) {\n return { injected: false, content };\n }\n\n // Find the register() function: export [async] function register(...) {\n const registerFnRegex = /export\\s+(?:async\\s+)?function\\s+register\\s*\\([^)]*\\)\\s*\\{/;\n const match = registerFnRegex.exec(content);\n\n if (!match) {\n return { injected: false, content };\n }\n\n // Determine indentation from the function body by looking at the first\n // indented line after the opening brace. Only capture spaces and tabs\n // (not newlines) to avoid blank lines corrupting the detected indent.\n // Default to 2-space indent (matches the scaffolded template).\n const afterBrace = content.slice(match.index + match[0].length);\n const indentMatch = /\\n([ \\t]+)/.exec(afterBrace);\n const indent = indentMatch ? indentMatch[1] : \" \";\n\n // Build the import line\n const importLine = 'import { registerGlasstrace } from \"@glasstrace/sdk\";\\n';\n\n // Check if the file already imports from @glasstrace/sdk\n const hasGlasstraceImport = content.includes(\"@glasstrace/sdk\");\n\n // Insert registerGlasstrace() as the first statement in the function body\n const insertPoint = match.index + match[0].length;\n const callInjection = `\\n${indent}// Glasstrace must be registered before other instrumentation\\n${indent}registerGlasstrace();\\n`;\n\n let modified: string;\n if (hasGlasstraceImport) {\n // File already imports from @glasstrace/sdk — check whether registerGlasstrace\n // is already among the specifiers to avoid producing a duplicate like\n // `import { registerGlasstrace, registerGlasstrace }`.\n const importRegex = /import\\s*\\{([^}]+)\\}\\s*from\\s*[\"']@glasstrace\\/sdk[\"']/;\n const importMatch = importRegex.exec(content);\n if (importMatch) {\n const specifiers = importMatch[1];\n const alreadyImported = specifiers\n .split(\",\")\n .some((s) => s.trim() === \"registerGlasstrace\");\n\n if (alreadyImported) {\n // Import already has registerGlasstrace — only inject the call\n modified = content.slice(0, insertPoint) + callInjection + content.slice(insertPoint);\n } else {\n // Add registerGlasstrace to existing import specifiers\n const existingImports = specifiers.trimEnd();\n const separator = existingImports.endsWith(\",\") ? \" \" : \", \";\n const updatedImport = `import { ${existingImports.trim()}${separator}registerGlasstrace } from \"@glasstrace/sdk\"`;\n modified = content.replace(importMatch[0], updatedImport);\n // Re-find the function in the shifted content and inject the call\n const newMatch = registerFnRegex.exec(modified);\n if (newMatch) {\n const newInsertPoint = newMatch.index + newMatch[0].length;\n modified = modified.slice(0, newInsertPoint) + callInjection + modified.slice(newInsertPoint);\n }\n }\n } else {\n // Non-destructured import (e.g., import * as sdk) — add a separate import\n modified = importLine + content;\n // Re-find the function in the shifted content and inject the call\n const newMatch = registerFnRegex.exec(modified);\n if (newMatch) {\n const newInsertPoint = newMatch.index + newMatch[0].length;\n modified = modified.slice(0, newInsertPoint) + callInjection + modified.slice(newInsertPoint);\n }\n }\n } else {\n // Add import at the top of the file and the call in the function body\n modified = importLine + content.slice(0, insertPoint) + callInjection + content.slice(insertPoint);\n }\n\n return { injected: true, content: modified };\n}\n\n/** Instrumentation filename variants Next.js recognizes, in priority order. */\nconst INSTRUMENTATION_FILENAMES = [\n \"instrumentation.ts\",\n \"instrumentation.js\",\n \"instrumentation.mjs\",\n] as const;\n\n/**\n * Result of {@link resolveInstrumentationTarget}. When the project has no\n * conflict, `target` identifies the file the scaffolder should create or\n * merge into. When both root and `src/` variants already exist, `target`\n * is `null` and both detected paths are returned so the caller can surface\n * a clear error — Next.js's loader behavior is undefined in that state\n * (DISC-493 Issue 1).\n */\nexport interface InstrumentationTarget {\n /** Absolute path of the chosen file, or null when a conflict exists. */\n target: string | null;\n /** Which layout was chosen. Null mirrors `target === null`. */\n layout: InstrumentationLayout | null;\n /**\n * Absolute paths of any existing instrumentation files detected. Includes\n * the chosen `target` when it exists, plus the competing file when there\n * is a conflict. Empty when the project has no instrumentation file yet.\n */\n existing: string[];\n /**\n * Absolute paths of root-level instrumentation files (e.g.,\n * `{projectRoot}/instrumentation.ts`). Tracked separately from `existing`\n * so callers can distinguish root from `src/` without string matching on\n * paths that may legitimately contain `src` elsewhere in their ancestry\n * (e.g., `/home/user/src/project/`).\n */\n rootExisting: string[];\n /**\n * Absolute paths of `src/`-level instrumentation files (e.g.,\n * `{projectRoot}/src/instrumentation.ts`). See `rootExisting` for the\n * rationale for tracking these separately.\n */\n srcExisting: string[];\n /**\n * When both a root and `src/` instrumentation file are present, Next.js's\n * behavior is not defined. The scaffolder refuses to create a third\n * competing file and asks the user to merge manually into the preferred\n * target (the `src/` variant when the project uses `src/` layout).\n */\n conflict: boolean;\n}\n\n/**\n * Detects whether the project uses Next.js's `src/` directory layout and\n * picks the instrumentation file path the scaffolder should create or\n * merge into.\n *\n * Selection rules (DISC-493 Issue 1):\n *\n * 1. Prefer an existing file. If `src/instrumentation.{ts,js,mjs}` exists,\n * it wins; otherwise the root variant wins. This preserves user intent\n * (they already chose a location) and matches what Next.js loads.\n * 2. When no instrumentation file exists yet, use `src/` when the project\n * has a `src/` directory at its root — the common Next.js convention.\n * 3. When both a root and a `src/` instrumentation file exist, return\n * `conflict: true`. Next.js's loader behavior is undefined in that\n * state and scaffolding a third write would mask whichever file Next.js\n * ultimately ignores.\n *\n * The resolver is pure: it reads the filesystem but writes nothing and\n * never throws. Callers can invoke it repeatedly (e.g., for validation).\n *\n * @param projectRoot - Absolute path to the project root directory.\n */\nexport function resolveInstrumentationTarget(\n projectRoot: string,\n): InstrumentationTarget {\n const rootExisting: string[] = [];\n const srcExisting: string[] = [];\n\n for (const name of INSTRUMENTATION_FILENAMES) {\n const rootPath = path.join(projectRoot, name);\n if (isRegularFile(rootPath)) {\n rootExisting.push(rootPath);\n }\n const srcPath = path.join(projectRoot, \"src\", name);\n if (isRegularFile(srcPath)) {\n srcExisting.push(srcPath);\n }\n }\n\n const existing = [...rootExisting, ...srcExisting];\n\n // Conflict: a file from each layout exists. Next.js's behavior is\n // undefined (DISC-493 Issue 1). The caller surfaces an error.\n if (rootExisting.length > 0 && srcExisting.length > 0) {\n return {\n target: null,\n layout: null,\n existing,\n rootExisting,\n srcExisting,\n conflict: true,\n };\n }\n\n // Prefer whichever layout already has an instrumentation file — the\n // user has already committed to that location and Next.js loads from it.\n if (srcExisting.length > 0) {\n return {\n target: srcExisting[0],\n layout: \"src\",\n existing,\n rootExisting,\n srcExisting,\n conflict: false,\n };\n }\n if (rootExisting.length > 0) {\n return {\n target: rootExisting[0],\n layout: \"root\",\n existing,\n rootExisting,\n srcExisting,\n conflict: false,\n };\n }\n\n // No file exists yet — default to `src/` when a `src/` directory is\n // present. Many Next.js apps use this layout and the bug in\n // DISC-493 was scaffolding to the root when Next.js ignores it.\n const srcDir = path.join(projectRoot, \"src\");\n const layout: InstrumentationLayout = isDirectory(srcDir) ? \"src\" : \"root\";\n const target = layout === \"src\"\n ? path.join(projectRoot, \"src\", \"instrumentation.ts\")\n : path.join(projectRoot, \"instrumentation.ts\");\n\n return {\n target,\n layout,\n existing,\n rootExisting,\n srcExisting,\n conflict: false,\n };\n}\n\n/** Returns true when `p` is a directory. Never throws. */\nfunction isDirectory(p: string): boolean {\n try {\n return fs.statSync(p).isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * Returns true when `p` is a regular file (not a directory, not missing).\n * Uses `lstatSync` so symlinks are evaluated as-is — a symlink to a file\n * counts, a symlink to a directory does not. Never throws.\n */\nfunction isRegularFile(p: string): boolean {\n try {\n const stat = fs.lstatSync(p);\n if (stat.isSymbolicLink()) {\n // Follow the symlink so we correctly classify links to real files.\n return fs.statSync(p).isFile();\n }\n return stat.isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Appends a new `export async function register()` to a file that has no\n * recognizable register function. Used when `src/instrumentation.ts`\n * exists (e.g., Sentry scaffolded it with only a top-level side-effect\n * import) but has no register hook yet.\n *\n * @internal Exported for unit testing only.\n */\nexport function appendRegisterFunction(content: string): string {\n const importLine = 'import { registerGlasstrace } from \"@glasstrace/sdk\";\\n';\n const functionBlock =\n \"\\n\" +\n \"export async function register() {\\n\" +\n \" // Glasstrace must be registered before Prisma instrumentation\\n\" +\n \" // to ensure all ORM spans are captured correctly.\\n\" +\n \" // If you use @prisma/instrumentation, import it after this call.\\n\" +\n \" registerGlasstrace();\\n\" +\n \"}\\n\";\n\n // Avoid a duplicate import if the file already pulls from @glasstrace/sdk.\n // When multiple specifiers are imported, splice registerGlasstrace in\n // rather than adding a second import line (mirrors injectRegisterGlasstrace).\n let withImport = content;\n const hasGlasstraceImport = content.includes(\"@glasstrace/sdk\");\n if (!hasGlasstraceImport) {\n withImport = importLine + content;\n } else {\n const importRegex = /import\\s*\\{([^}]+)\\}\\s*from\\s*[\"']@glasstrace\\/sdk[\"']/;\n const importMatch = importRegex.exec(content);\n if (importMatch) {\n const specifiers = importMatch[1];\n const alreadyImported = specifiers\n .split(\",\")\n .some((s) => s.trim() === \"registerGlasstrace\");\n if (!alreadyImported) {\n const existingImports = specifiers.trimEnd();\n const separator = existingImports.endsWith(\",\") ? \" \" : \", \";\n const updatedImport = `import { ${existingImports.trim()}${separator}registerGlasstrace } from \"@glasstrace/sdk\"`;\n withImport = content.replace(importMatch[0], updatedImport);\n }\n } else {\n // Non-destructured `import * as sdk from \"@glasstrace/sdk\"` form —\n // add a separate destructured import for registerGlasstrace so we\n // never depend on reading the namespace alias.\n withImport = importLine + content;\n }\n }\n\n // Ensure the file ends with a single newline before appending.\n const trailingNewline = withImport.endsWith(\"\\n\") ? \"\" : \"\\n\";\n return withImport + trailingNewline + functionBlock;\n}\n\n/**\n * Default `confirm`-style prompt used when `scaffoldInstrumentation` is\n * called without a `prompt` callback. Mirrors `init.ts#promptYesNo`:\n * returns the default value when stdin is not a TTY so non-interactive\n * shells do not hang.\n */\nasync function defaultInstrumentationPrompt(\n question: string,\n defaultValue: boolean,\n): Promise<boolean> {\n if (!process.stdin.isTTY) return defaultValue;\n const readline = await import(\"node:readline\");\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n return new Promise<boolean>((resolve) => {\n const suffix = defaultValue ? \" [Y/n] \" : \" [y/N] \";\n rl.question(question + suffix, (answer) => {\n rl.close();\n const trimmed = answer.trim().toLowerCase();\n if (trimmed === \"\") {\n resolve(defaultValue);\n return;\n }\n resolve(trimmed === \"y\" || trimmed === \"yes\");\n });\n });\n}\n\n/**\n * Ensures an instrumentation file exists and contains a `registerGlasstrace()`\n * call, merging into the user's existing file rather than overwriting it.\n *\n * Behavior (DISC-493 Issue 1):\n *\n * - Detects `src/`-layout projects via {@link resolveInstrumentationTarget}\n * and targets `src/instrumentation.ts` instead of the root when a `src/`\n * directory is present.\n * - When both `instrumentation.ts` and `src/instrumentation.ts` already\n * exist, returns `action: \"conflict\"` so the caller can emit a clear\n * error. Next.js's loader behavior is undefined in that state and\n * writing a third file would silently mask the one Next.js ignores.\n * - When the target does not exist, creates it with the standard template.\n * - When the target exists but has no `registerGlasstrace()` call:\n * - If it exposes an `export function register()`, injects the call as\n * the first statement (and imports `registerGlasstrace` if needed).\n * - If it has no register function, appends a new `export async function\n * register()` that calls `registerGlasstrace()` — this matches the\n * Sentry / Datadog / custom-instrumentation case where `register()`\n * hasn't been created yet.\n * - Before either mutation, prompts the user unless `force: true` is\n * passed (DISC-1247 Scenario 2c re-init safety).\n * - When the target already contains `registerGlasstrace()`, returns\n * `action: \"already-registered\"` (idempotent).\n *\n * @param projectRoot - Absolute path to the project root directory.\n * @param options - Prompt and force flags for merge-safe re-init.\n */\nexport async function scaffoldInstrumentation(\n projectRoot: string,\n options: ScaffoldInstrumentationOptions = {},\n): Promise<ScaffoldInstrumentationResult> {\n const target = resolveInstrumentationTarget(projectRoot);\n\n if (target.conflict) {\n return {\n action: \"conflict\",\n // Point the user at the `src/` variant — modern Next.js apps with a\n // `src/` directory load from there, so that's the merge target. The\n // competing path is reported separately for the error message.\n filePath: target.srcExisting[0],\n conflictingPath: target.rootExisting[0],\n };\n }\n\n const filePath = target.target;\n const layout = target.layout;\n // Defensive: resolver always sets these in the non-conflict path.\n if (filePath === null || layout === null) {\n return { action: \"unrecognized\" };\n }\n\n const force = options.force === true;\n const prompt = options.prompt ?? defaultInstrumentationPrompt;\n\n if (!fs.existsSync(filePath)) {\n const content = `import { registerGlasstrace } from \"@glasstrace/sdk\";\n\nexport async function register() {\n // Glasstrace must be registered before Prisma instrumentation\n // to ensure all ORM spans are captured correctly.\n // If you use @prisma/instrumentation, import it after this call.\n registerGlasstrace();\n}\n`;\n // Ensure the target directory exists (e.g., `src/` when the project\n // has the `src/` layout but no src/ folder somehow — defensive).\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, content, \"utf-8\");\n return { action: \"created\", filePath, layout };\n }\n\n // File exists — check whether registerGlasstrace() is already called.\n // Uses a helper that strips single-line comments before matching\n // so that `// registerGlasstrace()` is not treated as a real call.\n const existing = fs.readFileSync(filePath, \"utf-8\");\n\n if (hasRegisterGlasstraceCall(existing)) {\n return { action: \"already-registered\", filePath, layout };\n }\n\n // The file is going to change. Before writing, confirm with the user\n // unless --force was passed — otherwise a second init on a custom\n // instrumentation file would silently rewrite it. Non-interactive\n // shells (no TTY) skip the merge by default; pass `force: true` to\n // proceed without prompting, which the CLI does for `--yes`/`--force`.\n if (!force) {\n const approved = await prompt(\n `Merge registerGlasstrace() into ${path.relative(projectRoot, filePath)}?`,\n false,\n );\n if (!approved) {\n return { action: \"skipped\", filePath, layout };\n }\n }\n\n // Attempt injection into the existing register() function first.\n const injectResult = injectRegisterGlasstrace(existing);\n if (injectResult.injected) {\n fs.writeFileSync(filePath, injectResult.content, \"utf-8\");\n return { action: \"injected\", filePath, layout };\n }\n\n // No register() function present — append a fresh one. This is the\n // Sentry/Datadog case where `src/instrumentation.ts` exists with only\n // a top-level import or empty body.\n const appended = appendRegisterFunction(existing);\n fs.writeFileSync(filePath, appended, \"utf-8\");\n return { action: \"appended\", filePath, layout };\n}\n\n/**\n * Detects `next.config.js`, `next.config.ts`, or `next.config.mjs` and wraps\n * with `withGlasstraceConfig()`. If the config already contains\n * `withGlasstraceConfig`, the file is not modified.\n *\n * For CJS `.js` configs, adds a `require()` call and wraps `module.exports`.\n * The SDK ships dual ESM/CJS builds via tsup + conditional exports, so\n * `require(\"@glasstrace/sdk\")` resolves to the CJS entrypoint natively.\n *\n * @param projectRoot - Absolute path to the project root directory.\n * @returns A result object describing what happened, or `null` if no config\n * file was found at all.\n */\nexport async function scaffoldNextConfig(\n projectRoot: string,\n): Promise<ScaffoldNextConfigResult | null> {\n let configPath: string | undefined;\n let configName: string | undefined;\n\n for (const name of NEXT_CONFIG_NAMES) {\n const candidate = path.join(projectRoot, name);\n if (fs.existsSync(candidate)) {\n configPath = candidate;\n configName = name;\n break;\n }\n }\n\n if (configPath === undefined || configName === undefined) {\n return null;\n }\n\n const existing = fs.readFileSync(configPath, \"utf-8\");\n\n // Guard: empty or whitespace-only files have no export to wrap\n if (existing.trim().length === 0) {\n return { modified: false, reason: \"empty-file\" };\n }\n\n // Already wrapped — skip even in force mode to avoid double-wrapping\n if (existing.includes(\"withGlasstraceConfig\")) {\n return { modified: false, reason: \"already-wrapped\" };\n }\n\n const isESM = configName.endsWith(\".ts\") || configName.endsWith(\".mjs\");\n\n if (isESM) {\n // ESM: static import at top of file, wrap the export\n const importLine = 'import { withGlasstraceConfig } from \"@glasstrace/sdk\";\\n';\n const wrapResult = wrapExport(existing);\n if (!wrapResult.wrapped) {\n return { modified: false, reason: \"no-export\" };\n }\n const modified = importLine + \"\\n\" + wrapResult.content;\n fs.writeFileSync(configPath, modified, \"utf-8\");\n return { modified: true };\n }\n\n // CJS (.js): require() the SDK (resolves to the CJS dist build) and\n // wrap the module.exports expression in place — no file renaming needed.\n const requireLine = 'const { withGlasstraceConfig } = require(\"@glasstrace/sdk\");\\n';\n const wrapResult = wrapCJSExport(existing);\n if (!wrapResult.wrapped) {\n return { modified: false, reason: \"no-export\" };\n }\n const modified = requireLine + \"\\n\" + wrapResult.content;\n fs.writeFileSync(configPath, modified, \"utf-8\");\n return { modified: true };\n}\n\n/** @internal Exported for unit testing only. */\nexport interface WrapResult {\n content: string;\n wrapped: boolean;\n}\n\n/**\n * Wraps an ESM `export default` expression with `withGlasstraceConfig()`.\n *\n * Strategy: find the last `export default` in the file. Everything from\n * that statement to EOF is the exported expression. Strip optional trailing\n * semicolons/whitespace and wrap with `withGlasstraceConfig(...)`.\n *\n * @param content - The full file content containing an ESM default export.\n * @returns `{ wrapped: true, content }` on success, or `{ wrapped: false }` if\n * no recognizable export pattern was found (content returned unchanged).\n * @internal Exported for unit testing only.\n */\nexport function wrapExport(content: string): WrapResult {\n // Find the last `export default` — use lastIndexOf for robustness\n const marker = \"export default\";\n const idx = content.lastIndexOf(marker);\n if (idx === -1) {\n return { content, wrapped: false };\n }\n\n const preamble = content.slice(0, idx);\n const exprRaw = content.slice(idx + marker.length);\n // Trim leading whitespace; strip trailing semicolon + whitespace\n const expr = exprRaw.trim().replace(/;?\\s*$/, \"\");\n if (expr.length === 0) {\n return { content, wrapped: false };\n }\n\n return {\n content: preamble + `export default withGlasstraceConfig(${expr});\\n`,\n wrapped: true,\n };\n}\n\n/**\n * Wraps a CJS `module.exports = expr` with `withGlasstraceConfig()`.\n *\n * Strategy: find the last `module.exports =` in the file. Everything from\n * that statement to EOF is the exported expression. Strip optional trailing\n * semicolons/whitespace and wrap with `module.exports = withGlasstraceConfig(...)`.\n *\n * @param content - The full CJS file content containing `module.exports = ...`.\n * @returns `{ wrapped: true, content }` on success, or `{ wrapped: false }` if\n * no recognizable `module.exports` pattern was found (content returned unchanged).\n * @internal Exported for unit testing only.\n */\nexport function wrapCJSExport(content: string): WrapResult {\n const cjsMarker = \"module.exports\";\n const cjsIdx = content.lastIndexOf(cjsMarker);\n if (cjsIdx === -1) {\n return { content, wrapped: false };\n }\n\n const preamble = content.slice(0, cjsIdx);\n const afterMarker = content.slice(cjsIdx + cjsMarker.length);\n const eqMatch = /^\\s*=\\s*/.exec(afterMarker);\n if (!eqMatch) {\n return { content, wrapped: false };\n }\n\n const exprRaw = afterMarker.slice(eqMatch[0].length);\n const expr = exprRaw.trim().replace(/;?\\s*$/, \"\");\n if (expr.length === 0) {\n return { content, wrapped: false };\n }\n\n return {\n content: preamble + `module.exports = withGlasstraceConfig(${expr});\\n`,\n wrapped: true,\n };\n}\n\n/**\n * Creates `.env.local` with `GLASSTRACE_API_KEY=` placeholder, or appends\n * to an existing file if it does not already contain `GLASSTRACE_API_KEY`.\n *\n * Preservation behavior (DISC-1247 Scenario 6): if an existing `.env.local`\n * already defines a developer key (`gt_dev_*`), the file is left untouched\n * so re-running `init` after an account claim does not overwrite the\n * claimed dev key.\n *\n * @param projectRoot - Absolute path to the project root directory.\n * @returns True if the file was created or modified, false if already configured.\n */\nexport async function scaffoldEnvLocal(projectRoot: string): Promise<boolean> {\n const filePath = path.join(projectRoot, \".env.local\");\n\n if (fs.existsSync(filePath)) {\n const existing = fs.readFileSync(filePath, \"utf-8\");\n if (/^\\s*#?\\s*GLASSTRACE_API_KEY\\s*=/m.test(existing)) {\n return false;\n }\n // Append with a newline separator if needed\n const separator = existing.endsWith(\"\\n\") ? \"\" : \"\\n\";\n fs.writeFileSync(filePath, existing + separator + \"# GLASSTRACE_API_KEY=your_key_here\\n\", \"utf-8\");\n return true;\n }\n\n fs.writeFileSync(filePath, \"# GLASSTRACE_API_KEY=your_key_here\\n\", \"utf-8\");\n return true;\n}\n\n/**\n * Adds `GLASSTRACE_COVERAGE_MAP=true` to `.env.local`.\n * Creates the file if it does not exist. If the key is already present\n * with a value other than `true`, it is updated in place.\n *\n * @param projectRoot - Absolute path to the project root directory.\n * @returns True if the file was created or modified, false if already set to `true`.\n */\nexport async function addCoverageMapEnv(projectRoot: string): Promise<boolean> {\n const filePath = path.join(projectRoot, \".env.local\");\n\n if (!fs.existsSync(filePath)) {\n fs.writeFileSync(filePath, \"GLASSTRACE_COVERAGE_MAP=true\\n\", \"utf-8\");\n return true;\n }\n\n const existing = fs.readFileSync(filePath, \"utf-8\");\n const keyRegex = /^(\\s*GLASSTRACE_COVERAGE_MAP\\s*=\\s*)(.*)$/m;\n const keyMatch = keyRegex.exec(existing);\n\n if (keyMatch) {\n const currentValue = keyMatch[2].trim();\n if (currentValue === \"true\") {\n // Already set to true — nothing to do\n return false;\n }\n // Key exists but is not `true` — update in place\n const updated = existing.replace(keyRegex, `${keyMatch[1]}true`);\n fs.writeFileSync(filePath, updated, \"utf-8\");\n return true;\n }\n\n const separator = existing.endsWith(\"\\n\") ? \"\" : \"\\n\";\n fs.writeFileSync(filePath, existing + separator + \"GLASSTRACE_COVERAGE_MAP=true\\n\", \"utf-8\");\n return true;\n}\n\n/**\n * Adds `.glasstrace/` to `.gitignore`, or creates `.gitignore` if missing.\n * Does not add duplicate entries.\n *\n * @param projectRoot - Absolute path to the project root directory.\n * @returns True if the file was created or modified, false if already configured.\n */\nexport async function scaffoldGitignore(projectRoot: string): Promise<boolean> {\n const filePath = path.join(projectRoot, \".gitignore\");\n\n if (fs.existsSync(filePath)) {\n const existing = fs.readFileSync(filePath, \"utf-8\");\n // Check line-by-line to avoid false positive partial matches\n const lines = existing.split(\"\\n\").map((l) => l.trim());\n if (lines.includes(\".glasstrace/\")) {\n return false;\n }\n const separator = existing.endsWith(\"\\n\") ? \"\" : \"\\n\";\n fs.writeFileSync(filePath, existing + separator + \".glasstrace/\\n\", \"utf-8\");\n return true;\n }\n\n fs.writeFileSync(filePath, \".glasstrace/\\n\", \"utf-8\");\n return true;\n}\n\n/**\n * Creates or updates the `.glasstrace/mcp-connected` marker.\n *\n * The marker is a v2 record that captures the source and identity\n * fingerprint of the credential currently embedded in the managed\n * MCP config. The nudge system reads it to suppress \"MCP not\n * configured\" prompts; `mcp add` reads it to detect when the\n * effective credential has drifted from what `mcp.json` was last\n * written with and a refresh is needed.\n *\n * Backwards-compatible signature: passing an anonymous key writes a\n * v2 marker with `credentialSource: \"anon\"`. The reader handles\n * pre-existing v1 markers transparently. v3+ and corrupted markers\n * are unconditionally overwritten.\n *\n * @param projectRoot - Absolute path to the project root directory.\n * @param anonKey - The anonymous API key whose fingerprint will be\n * recorded as the active credential identity.\n * @returns True if the marker was created or updated, false if an\n * existing v1/v2 marker already records the same identity.\n */\nexport async function scaffoldMcpMarker(\n projectRoot: string,\n anonKey: string,\n): Promise<boolean> {\n return writeMcpMarker(projectRoot, {\n credentialSource: \"anon\",\n credentialHash: identityFingerprint(anonKey),\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,YAAYA,SAAQ;AACpB,YAAYC,WAAU;AACtB,YAAY,cAAc;;;ACH1B,YAAY,QAAQ;AACpB,YAAY,UAAU;AAcf,SAAS,0BAA0B,SAA0B;AAClE,SAAO,QAAQ,MAAM,IAAI,EAAE,KAAK,CAAC,SAAS;AACxC,UAAM,cAAc,KAAK,QAAQ,WAAW,EAAE;AAC9C,WAAO,4BAA4B,KAAK,WAAW;AAAA,EACrD,CAAC;AACH;AAyFO,SAAS,yBAAyB,SAA+B;AAItE,MAAI,0BAA0B,OAAO,GAAG;AACtC,WAAO,EAAE,UAAU,OAAO,QAAQ;AAAA,EACpC;AAGA,QAAM,kBAAkB;AACxB,QAAM,QAAQ,gBAAgB,KAAK,OAAO;AAE1C,MAAI,CAAC,OAAO;AACV,WAAO,EAAE,UAAU,OAAO,QAAQ;AAAA,EACpC;AAMA,QAAM,aAAa,QAAQ,MAAM,MAAM,QAAQ,MAAM,CAAC,EAAE,MAAM;AAC9D,QAAM,cAAc,aAAa,KAAK,UAAU;AAChD,QAAM,SAAS,cAAc,YAAY,CAAC,IAAI;AAG9C,QAAM,aAAa;AAGnB,QAAM,sBAAsB,QAAQ,SAAS,iBAAiB;AAG9D,QAAM,cAAc,MAAM,QAAQ,MAAM,CAAC,EAAE;AAC3C,QAAM,gBAAgB;AAAA,EAAK,MAAM;AAAA,EAAkE,MAAM;AAAA;AAEzG,MAAI;AACJ,MAAI,qBAAqB;AAIvB,UAAM,cAAc;AACpB,UAAM,cAAc,YAAY,KAAK,OAAO;AAC5C,QAAI,aAAa;AACf,YAAM,aAAa,YAAY,CAAC;AAChC,YAAM,kBAAkB,WACrB,MAAM,GAAG,EACT,KAAK,CAAC,MAAM,EAAE,KAAK,MAAM,oBAAoB;AAEhD,UAAI,iBAAiB;AAEnB,mBAAW,QAAQ,MAAM,GAAG,WAAW,IAAI,gBAAgB,QAAQ,MAAM,WAAW;AAAA,MACtF,OAAO;AAEL,cAAM,kBAAkB,WAAW,QAAQ;AAC3C,cAAM,YAAY,gBAAgB,SAAS,GAAG,IAAI,MAAM;AACxD,cAAM,gBAAgB,YAAY,gBAAgB,KAAK,CAAC,GAAG,SAAS;AACpE,mBAAW,QAAQ,QAAQ,YAAY,CAAC,GAAG,aAAa;AAExD,cAAM,WAAW,gBAAgB,KAAK,QAAQ;AAC9C,YAAI,UAAU;AACZ,gBAAM,iBAAiB,SAAS,QAAQ,SAAS,CAAC,EAAE;AACpD,qBAAW,SAAS,MAAM,GAAG,cAAc,IAAI,gBAAgB,SAAS,MAAM,cAAc;AAAA,QAC9F;AAAA,MACF;AAAA,IACF,OAAO;AAEL,iBAAW,aAAa;AAExB,YAAM,WAAW,gBAAgB,KAAK,QAAQ;AAC9C,UAAI,UAAU;AACZ,cAAM,iBAAiB,SAAS,QAAQ,SAAS,CAAC,EAAE;AACpD,mBAAW,SAAS,MAAM,GAAG,cAAc,IAAI,gBAAgB,SAAS,MAAM,cAAc;AAAA,MAC9F;AAAA,IACF;AAAA,EACF,OAAO;AAEL,eAAW,aAAa,QAAQ,MAAM,GAAG,WAAW,IAAI,gBAAgB,QAAQ,MAAM,WAAW;AAAA,EACnG;AAEA,SAAO,EAAE,UAAU,MAAM,SAAS,SAAS;AAC7C;AAGA,IAAM,4BAA4B;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AACF;AAkEO,SAAS,6BACd,aACuB;AACvB,QAAM,eAAyB,CAAC;AAChC,QAAM,cAAwB,CAAC;AAE/B,aAAW,QAAQ,2BAA2B;AAC5C,UAAM,WAAgB,UAAK,aAAa,IAAI;AAC5C,QAAI,cAAc,QAAQ,GAAG;AAC3B,mBAAa,KAAK,QAAQ;AAAA,IAC5B;AACA,UAAM,UAAe,UAAK,aAAa,OAAO,IAAI;AAClD,QAAI,cAAc,OAAO,GAAG;AAC1B,kBAAY,KAAK,OAAO;AAAA,IAC1B;AAAA,EACF;AAEA,QAAM,WAAW,CAAC,GAAG,cAAc,GAAG,WAAW;AAIjD,MAAI,aAAa,SAAS,KAAK,YAAY,SAAS,GAAG;AACrD,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU;AAAA,IACZ;AAAA,EACF;AAIA,MAAI,YAAY,SAAS,GAAG;AAC1B,WAAO;AAAA,MACL,QAAQ,YAAY,CAAC;AAAA,MACrB,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU;AAAA,IACZ;AAAA,EACF;AACA,MAAI,aAAa,SAAS,GAAG;AAC3B,WAAO;AAAA,MACL,QAAQ,aAAa,CAAC;AAAA,MACtB,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU;AAAA,IACZ;AAAA,EACF;AAKA,QAAM,SAAc,UAAK,aAAa,KAAK;AAC3C,QAAM,SAAgC,YAAY,MAAM,IAAI,QAAQ;AACpE,QAAM,SAAS,WAAW,QACjB,UAAK,aAAa,OAAO,oBAAoB,IAC7C,UAAK,aAAa,oBAAoB;AAE/C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,EACZ;AACF;AAGA,SAAS,YAAY,GAAoB;AACvC,MAAI;AACF,WAAU,YAAS,CAAC,EAAE,YAAY;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOA,SAAS,cAAc,GAAoB;AACzC,MAAI;AACF,UAAM,OAAU,aAAU,CAAC;AAC3B,QAAI,KAAK,eAAe,GAAG;AAEzB,aAAU,YAAS,CAAC,EAAE,OAAO;AAAA,IAC/B;AACA,WAAO,KAAK,OAAO;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAUO,SAAS,uBAAuB,SAAyB;AAC9D,QAAM,aAAa;AACnB,QAAM,gBACJ;AAWF,MAAI,aAAa;AACjB,QAAM,sBAAsB,QAAQ,SAAS,iBAAiB;AAC9D,MAAI,CAAC,qBAAqB;AACxB,iBAAa,aAAa;AAAA,EAC5B,OAAO;AACL,UAAM,cAAc;AACpB,UAAM,cAAc,YAAY,KAAK,OAAO;AAC5C,QAAI,aAAa;AACf,YAAM,aAAa,YAAY,CAAC;AAChC,YAAM,kBAAkB,WACrB,MAAM,GAAG,EACT,KAAK,CAAC,MAAM,EAAE,KAAK,MAAM,oBAAoB;AAChD,UAAI,CAAC,iBAAiB;AACpB,cAAM,kBAAkB,WAAW,QAAQ;AAC3C,cAAM,YAAY,gBAAgB,SAAS,GAAG,IAAI,MAAM;AACxD,cAAM,gBAAgB,YAAY,gBAAgB,KAAK,CAAC,GAAG,SAAS;AACpE,qBAAa,QAAQ,QAAQ,YAAY,CAAC,GAAG,aAAa;AAAA,MAC5D;AAAA,IACF,OAAO;AAIL,mBAAa,aAAa;AAAA,IAC5B;AAAA,EACF;AAGA,QAAM,kBAAkB,WAAW,SAAS,IAAI,IAAI,KAAK;AACzD,SAAO,aAAa,kBAAkB;AACxC;AAQA,eAAe,6BACb,UACA,cACkB;AAClB,MAAI,CAAC,QAAQ,MAAM,MAAO,QAAO;AACjC,QAAMC,YAAW,MAAM,OAAO,eAAe;AAC7C,QAAM,KAAKA,UAAS,gBAAgB;AAAA,IAClC,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,EAClB,CAAC;AACD,SAAO,IAAI,QAAiB,CAAC,YAAY;AACvC,UAAM,SAAS,eAAe,YAAY;AAC1C,OAAG,SAAS,WAAW,QAAQ,CAAC,WAAW;AACzC,SAAG,MAAM;AACT,YAAM,UAAU,OAAO,KAAK,EAAE,YAAY;AAC1C,UAAI,YAAY,IAAI;AAClB,gBAAQ,YAAY;AACpB;AAAA,MACF;AACA,cAAQ,YAAY,OAAO,YAAY,KAAK;AAAA,IAC9C,CAAC;AAAA,EACH,CAAC;AACH;AA+BA,eAAsB,wBACpB,aACA,UAA0C,CAAC,GACH;AACxC,QAAM,SAAS,6BAA6B,WAAW;AAEvD,MAAI,OAAO,UAAU;AACnB,WAAO;AAAA,MACL,QAAQ;AAAA;AAAA;AAAA;AAAA,MAIR,UAAU,OAAO,YAAY,CAAC;AAAA,MAC9B,iBAAiB,OAAO,aAAa,CAAC;AAAA,IACxC;AAAA,EACF;AAEA,QAAM,WAAW,OAAO;AACxB,QAAM,SAAS,OAAO;AAEtB,MAAI,aAAa,QAAQ,WAAW,MAAM;AACxC,WAAO,EAAE,QAAQ,eAAe;AAAA,EAClC;AAEA,QAAM,QAAQ,QAAQ,UAAU;AAChC,QAAM,SAAS,QAAQ,UAAU;AAEjC,MAAI,CAAI,cAAW,QAAQ,GAAG;AAC5B,UAAM,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWhB,IAAG,aAAe,aAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AACxD,IAAG,iBAAc,UAAU,SAAS,OAAO;AAC3C,WAAO,EAAE,QAAQ,WAAW,UAAU,OAAO;AAAA,EAC/C;AAKA,QAAM,WAAc,gBAAa,UAAU,OAAO;AAElD,MAAI,0BAA0B,QAAQ,GAAG;AACvC,WAAO,EAAE,QAAQ,sBAAsB,UAAU,OAAO;AAAA,EAC1D;AAOA,MAAI,CAAC,OAAO;AACV,UAAM,WAAW,MAAM;AAAA,MACrB,mCAAwC,cAAS,aAAa,QAAQ,CAAC;AAAA,MACvE;AAAA,IACF;AACA,QAAI,CAAC,UAAU;AACb,aAAO,EAAE,QAAQ,WAAW,UAAU,OAAO;AAAA,IAC/C;AAAA,EACF;AAGA,QAAM,eAAe,yBAAyB,QAAQ;AACtD,MAAI,aAAa,UAAU;AACzB,IAAG,iBAAc,UAAU,aAAa,SAAS,OAAO;AACxD,WAAO,EAAE,QAAQ,YAAY,UAAU,OAAO;AAAA,EAChD;AAKA,QAAM,WAAW,uBAAuB,QAAQ;AAChD,EAAG,iBAAc,UAAU,UAAU,OAAO;AAC5C,SAAO,EAAE,QAAQ,YAAY,UAAU,OAAO;AAChD;AAeA,eAAsB,mBACpB,aAC0C;AAC1C,MAAI;AACJ,MAAI;AAEJ,aAAW,QAAQ,mBAAmB;AACpC,UAAM,YAAiB,UAAK,aAAa,IAAI;AAC7C,QAAO,cAAW,SAAS,GAAG;AAC5B,mBAAa;AACb,mBAAa;AACb;AAAA,IACF;AAAA,EACF;AAEA,MAAI,eAAe,UAAa,eAAe,QAAW;AACxD,WAAO;AAAA,EACT;AAEA,QAAM,WAAc,gBAAa,YAAY,OAAO;AAGpD,MAAI,SAAS,KAAK,EAAE,WAAW,GAAG;AAChC,WAAO,EAAE,UAAU,OAAO,QAAQ,aAAa;AAAA,EACjD;AAGA,MAAI,SAAS,SAAS,sBAAsB,GAAG;AAC7C,WAAO,EAAE,UAAU,OAAO,QAAQ,kBAAkB;AAAA,EACtD;AAEA,QAAM,QAAQ,WAAW,SAAS,KAAK,KAAK,WAAW,SAAS,MAAM;AAEtE,MAAI,OAAO;AAET,UAAM,aAAa;AACnB,UAAMC,cAAa,WAAW,QAAQ;AACtC,QAAI,CAACA,YAAW,SAAS;AACvB,aAAO,EAAE,UAAU,OAAO,QAAQ,YAAY;AAAA,IAChD;AACA,UAAMC,YAAW,aAAa,OAAOD,YAAW;AAChD,IAAG,iBAAc,YAAYC,WAAU,OAAO;AAC9C,WAAO,EAAE,UAAU,KAAK;AAAA,EAC1B;AAIA,QAAM,cAAc;AACpB,QAAM,aAAa,cAAc,QAAQ;AACzC,MAAI,CAAC,WAAW,SAAS;AACvB,WAAO,EAAE,UAAU,OAAO,QAAQ,YAAY;AAAA,EAChD;AACA,QAAM,WAAW,cAAc,OAAO,WAAW;AACjD,EAAG,iBAAc,YAAY,UAAU,OAAO;AAC9C,SAAO,EAAE,UAAU,KAAK;AAC1B;AAoBO,SAAS,WAAW,SAA6B;AAEtD,QAAM,SAAS;AACf,QAAM,MAAM,QAAQ,YAAY,MAAM;AACtC,MAAI,QAAQ,IAAI;AACd,WAAO,EAAE,SAAS,SAAS,MAAM;AAAA,EACnC;AAEA,QAAM,WAAW,QAAQ,MAAM,GAAG,GAAG;AACrC,QAAM,UAAU,QAAQ,MAAM,MAAM,OAAO,MAAM;AAEjD,QAAM,OAAO,QAAQ,KAAK,EAAE,QAAQ,UAAU,EAAE;AAChD,MAAI,KAAK,WAAW,GAAG;AACrB,WAAO,EAAE,SAAS,SAAS,MAAM;AAAA,EACnC;AAEA,SAAO;AAAA,IACL,SAAS,WAAW,uCAAuC,IAAI;AAAA;AAAA,IAC/D,SAAS;AAAA,EACX;AACF;AAcO,SAAS,cAAc,SAA6B;AACzD,QAAM,YAAY;AAClB,QAAM,SAAS,QAAQ,YAAY,SAAS;AAC5C,MAAI,WAAW,IAAI;AACjB,WAAO,EAAE,SAAS,SAAS,MAAM;AAAA,EACnC;AAEA,QAAM,WAAW,QAAQ,MAAM,GAAG,MAAM;AACxC,QAAM,cAAc,QAAQ,MAAM,SAAS,UAAU,MAAM;AAC3D,QAAM,UAAU,WAAW,KAAK,WAAW;AAC3C,MAAI,CAAC,SAAS;AACZ,WAAO,EAAE,SAAS,SAAS,MAAM;AAAA,EACnC;AAEA,QAAM,UAAU,YAAY,MAAM,QAAQ,CAAC,EAAE,MAAM;AACnD,QAAM,OAAO,QAAQ,KAAK,EAAE,QAAQ,UAAU,EAAE;AAChD,MAAI,KAAK,WAAW,GAAG;AACrB,WAAO,EAAE,SAAS,SAAS,MAAM;AAAA,EACnC;AAEA,SAAO;AAAA,IACL,SAAS,WAAW,yCAAyC,IAAI;AAAA;AAAA,IACjE,SAAS;AAAA,EACX;AACF;AAcA,eAAsB,iBAAiB,aAAuC;AAC5E,QAAM,WAAgB,UAAK,aAAa,YAAY;AAEpD,MAAO,cAAW,QAAQ,GAAG;AAC3B,UAAM,WAAc,gBAAa,UAAU,OAAO;AAClD,QAAI,mCAAmC,KAAK,QAAQ,GAAG;AACrD,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,SAAS,SAAS,IAAI,IAAI,KAAK;AACjD,IAAG,iBAAc,UAAU,WAAW,YAAY,wCAAwC,OAAO;AACjG,WAAO;AAAA,EACT;AAEA,EAAG,iBAAc,UAAU,wCAAwC,OAAO;AAC1E,SAAO;AACT;AAUA,eAAsB,kBAAkB,aAAuC;AAC7E,QAAM,WAAgB,UAAK,aAAa,YAAY;AAEpD,MAAI,CAAI,cAAW,QAAQ,GAAG;AAC5B,IAAG,iBAAc,UAAU,kCAAkC,OAAO;AACpE,WAAO;AAAA,EACT;AAEA,QAAM,WAAc,gBAAa,UAAU,OAAO;AAClD,QAAM,WAAW;AACjB,QAAM,WAAW,SAAS,KAAK,QAAQ;AAEvC,MAAI,UAAU;AACZ,UAAM,eAAe,SAAS,CAAC,EAAE,KAAK;AACtC,QAAI,iBAAiB,QAAQ;AAE3B,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,SAAS,QAAQ,UAAU,GAAG,SAAS,CAAC,CAAC,MAAM;AAC/D,IAAG,iBAAc,UAAU,SAAS,OAAO;AAC3C,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,SAAS,SAAS,IAAI,IAAI,KAAK;AACjD,EAAG,iBAAc,UAAU,WAAW,YAAY,kCAAkC,OAAO;AAC3F,SAAO;AACT;AASA,eAAsB,kBAAkB,aAAuC;AAC7E,QAAM,WAAgB,UAAK,aAAa,YAAY;AAEpD,MAAO,cAAW,QAAQ,GAAG;AAC3B,UAAM,WAAc,gBAAa,UAAU,OAAO;AAElD,UAAM,QAAQ,SAAS,MAAM,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AACtD,QAAI,MAAM,SAAS,cAAc,GAAG;AAClC,aAAO;AAAA,IACT;AACA,UAAM,YAAY,SAAS,SAAS,IAAI,IAAI,KAAK;AACjD,IAAG,iBAAc,UAAU,WAAW,YAAY,kBAAkB,OAAO;AAC3E,WAAO;AAAA,EACT;AAEA,EAAG,iBAAc,UAAU,kBAAkB,OAAO;AACpD,SAAO;AACT;;;ADtuBO,SAAS,iBAAiB,UAA2B;AAC1D,QAAM,CAAC,KAAK,IAAI,QAAQ,SAAS,KAAK,MAAM,GAAG,EAAE,IAAI,MAAM;AAC3D,SAAO,SAAS;AAClB;AAyCA,eAAsB,sBAAsB,SAOM;AAChD,QAAM,EAAE,YAAY,iBAAiB,MAAM,IAAI;AAC/C,MAAI,eAAe,KAAM,QAAO;AAEhC,QAAM,SAAS,QAAQ,cAAiB;AACxC,QAAM,OAAO,QAAQ,aAAa,CAAC,MAAiB,iBAAa,GAAG,OAAO;AAC3E,QAAM,SAAS,QAAQ,UAAU;AAEjC,MAAI,CAAC,OAAO,UAAU,EAAG,QAAO;AAEhC,MAAI;AACJ,MAAI;AACF,sBAAkB,KAAK,UAAU;AAAA,EACnC,QAAQ;AAIN,WAAO;AAAA,EACT;AAEA,MAAI,iBAAiB,iBAAiB,eAAe,GAAG;AACtD,WAAO;AAAA,EACT;AAEA,MAAI,OAAO;AACT,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,MAAM;AAAA,IACnB,0BAA0B,UAAU;AAAA,IACpC;AAAA,EACF;AACA,SAAO,SAAS,oBAAoB;AACtC;AAMA,eAAe,YAAY,UAAkB,cAAyC;AACpF,MAAI,CAAC,QAAQ,MAAM,OAAO;AACxB,WAAO;AAAA,EACT;AAEA,QAAM,KAAc,yBAAgB;AAAA,IAClC,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAED,SAAO,IAAI,QAAiB,CAAC,YAAY;AACvC,UAAM,SAAS,eAAe,YAAY;AAC1C,OAAG,SAAS,WAAW,QAAQ,CAAC,WAAW;AACzC,SAAG,MAAM;AACT,YAAM,UAAU,OAAO,KAAK,EAAE,YAAY;AAC1C,UAAI,YAAY,IAAI;AAClB,gBAAQ,YAAY;AACpB;AAAA,MACF;AACA,cAAQ,YAAY,OAAO,YAAY,KAAK;AAAA,IAC9C,CAAC;AAAA,EACH,CAAC;AACH;AAsCA,SAAS,uBAAuB,SAAyB;AACvD,SAAO,QAAQ,QAAQ,WAAW,IAAI;AACxC;AA+CO,SAAS,+BACd,kBACA,QACS;AACT,QAAM,aAAa,WAAW,WAAW,WAAW;AAIpD,QAAM,cAAc,CAAC,YAAY,GAAG,UAAU,GAAG;AACjD,QAAM,mBAAmB;AAAA,IACvB,GAAG,UAAU;AAAA,IACb,GAAG,UAAU;AAAA,IACb;AAAA,IACA;AAAA,EACF;AACA,QAAM,cAAc;AAAA,IAClB,GAAG,UAAU;AAAA,IACb;AAAA,EACF;AAKA,MAAI,cAAc;AAClB,MAAI,mBAAmB;AACvB,MAAI,cAAc;AAElB,aAAW,WAAW,iBAAiB,MAAM,IAAI,GAAG;AAClD,UAAM,OAAO,QAAQ,KAAK;AAC1B,QAAI,SAAS,MAAM,KAAK,WAAW,GAAG,EAAG;AAEzC,UAAM,WAAW,KAAK,WAAW,GAAG;AACpC,UAAM,UAAU,WAAW,KAAK,MAAM,CAAC,EAAE,KAAK,IAAI;AAClD,QAAI,YAAY,GAAI;AAIpB,UAAM,aAAa,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AAEhE,UAAM,cAAc,qBAAqB,YAAY,WAAW;AAChE,UAAM,mBAAmB,qBAAqB,YAAY,gBAAgB;AAC1E,UAAM,cAAc,qBAAqB,YAAY,WAAW;AAEhE,QAAI,CAAC,eAAe,CAAC,oBAAoB,CAAC,YAAa;AAEvD,UAAM,WAAW,CAAC;AAClB,QAAI,YAAa,eAAc;AAC/B,QAAI,iBAAkB,oBAAmB;AACzC,QAAI,YAAa,eAAc;AAAA,EACjC;AAKA,SAAO,eAAe,oBAAoB;AAC5C;AAaA,SAAS,qBACP,SACA,SACS;AAGT,QAAM,OAAO,QAAQ,SAAS,GAAG,IAAI,QAAQ,MAAM,GAAG,EAAE,IAAI;AAE5D,aAAW,UAAU,SAAS;AAC5B,UAAM,QAAQ,OAAO,SAAS,GAAG,IAAI,OAAO,MAAM,GAAG,EAAE,IAAI;AAC3D,QAAI,SAAS,MAAO,QAAO;AAE3B,QAAI,QAAQ,WAAW,KAAK,GAAG;AAC7B,YAAM,SAAS,QAAQ,MAAM,CAAC;AAC9B,YAAM,QAAQ,OAAO,SAAS,GAAG,IAAI,OAAO,MAAM,GAAG,EAAE,IAAI;AAC3D,UAAI,UAAU,SAAS,MAAM,SAAS,IAAI,KAAK,EAAE,EAAG,QAAO;AAAA,IAC7D;AAAA,EACF;AACA,SAAO;AACT;AASA,eAAsB,cACpB,OACA,aACA,OACe;AACf,aAAW,QAAQ,CAAC,GAAG,KAAK,EAAE,QAAQ,GAAG;AACvC,QAAI;AACF,cAAQ,MAAM;AAAA,QACZ,KAAK,mBAAmB;AAKtB,gBAAM,YACJ,OAAO,uBAA4B,WAAK,aAAa,oBAAoB;AAC3E,cAAO,eAAW,SAAS,GAAG;AAC5B,kBAAM,UAAa,iBAAa,WAAW,OAAO;AAClD,gBAAI,6BAA6B,OAAO,GAAG;AACzC,cAAG,eAAW,SAAS;AAAA,YACzB,WAAW,OAAO,mCAAmC,QAAW;AAG9D,cAAG,kBAAc,WAAW,MAAM,gCAAgC,OAAO;AAAA,YAC3E,OAAO;AACL,oBAAM,UAAU,yBAAyB,OAAO;AAChD,kBAAI,YAAY,SAAS;AACvB,gBAAG,kBAAc,WAAW,SAAS,OAAO;AAAA,cAC9C;AAAA,YACF;AAAA,UACF;AACA;AAAA,QACF;AAAA,QACA,KAAK,eAAe;AAClB,qBAAW,QAAQ,mBAAmB;AACpC,kBAAM,aAAkB,WAAK,aAAa,IAAI;AAC9C,gBAAI,CAAI,eAAW,UAAU,GAAG;AAC9B;AAAA,YACF;AACA,kBAAM,UAAa,iBAAa,YAAY,OAAO;AACnD,gBAAI,CAAC,QAAQ,SAAS,sBAAsB,GAAG;AAC7C;AAAA,YACF;AACA,kBAAM,QAAQ,KAAK,SAAS,KAAK,KAAK,KAAK,SAAS,MAAM;AAC1D,kBAAM,eAAe,QACjB,aAAa,OAAO,IACpB,gBAAgB,OAAO;AAC3B,gBAAI,aAAa,WAAW;AAC1B,oBAAM,UAAU,6BAA6B,aAAa,OAAO;AACjE,cAAG,kBAAc,YAAY,uBAAuB,OAAO,GAAG,OAAO;AAAA,YACvE;AACA;AAAA,UACF;AACA;AAAA,QACF;AAAA,QACA,KAAK,aAAa;AAKhB,gBAAM,UAAe,WAAK,aAAa,YAAY;AACnD,cAAO,eAAW,OAAO,GAAG;AAC1B,kBAAM,UAAa,iBAAa,SAAS,OAAO;AAChD,kBAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,kBAAM,WAAW,MAAM,OAAO,CAAC,SAAS;AACtC,oBAAM,UAAU,KAAK,KAAK;AAC1B,qBAAO,CAAC,kCAAkC,KAAK,OAAO;AAAA,YACxD,CAAC;AACD,gBAAI,SAAS,WAAW,MAAM,QAAQ;AACpC,oBAAM,SAAS,SAAS,KAAK,IAAI;AACjC,kBAAI,OAAO,KAAK,EAAE,WAAW,GAAG;AAC9B,gBAAG,eAAW,OAAO;AAAA,cACvB,OAAO;AACL,gBAAG,kBAAc,SAAS,QAAQ,OAAO;AAAA,cAC3C;AAAA,YACF;AAAA,UACF;AACA;AAAA,QACF;AAAA,QACA,KAAK,aAAa;AAChB,gBAAM,gBAAqB,WAAK,aAAa,YAAY;AACzD,cAAO,eAAW,aAAa,GAAG;AAChC,kBAAM,UAAa,iBAAa,eAAe,OAAO;AACtD,kBAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,kBAAM,WAAW,MAAM;AAAA,cACrB,CAAC,SAAS,KAAK,KAAK,MAAM;AAAA,YAC5B;AACA,gBAAI,SAAS,WAAW,MAAM,QAAQ;AACpC,oBAAM,SAAS,SAAS,KAAK,IAAI;AACjC,kBAAI,OAAO,KAAK,EAAE,WAAW,GAAG;AAC9B,gBAAG,eAAW,aAAa;AAAA,cAC7B,OAAO;AACL,gBAAG,kBAAc,eAAe,QAAQ,OAAO;AAAA,cACjD;AAAA,YACF;AAAA,UACF;AACA;AAAA,QACF;AAAA,QACA,KAAK,kBAAkB;AAIrB,8BAAoB,WAAW;AAC/B;AAAA,QACF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAMA,eAAsB,QAAQ,SAA2C;AACvE,QAAM,EAAE,KAAK,YAAY,IAAI;AAC7B,QAAM,UAAoB,CAAC;AAC3B,QAAM,WAAqB,CAAC;AAC5B,QAAM,SAAmB,CAAC;AAG1B,MAAI;AACJ,MAAI;AACF,UAAM,iBAAiB,mBAAmB,QAAQ,WAAW;AAC7D,kBAAc,eAAe;AAC7B,QAAI,eAAe,cAAc,eAAe,iBAAiB;AAC/D,cAAQ,KAAK,wBAAwB,eAAe,eAAe,0BAAqB;AAAA,IAC1F;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC5D,WAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,EAClD;AAGA,QAAM,kBAAuB,WAAK,aAAa,cAAc;AAC7D,MAAI,CAAI,eAAW,eAAe,GAAG;AACnC,WAAO,KAAK,sEAAsE;AAClF,WAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,EAClD;AAKA,QAAM,gBAA+B,EAAE,OAAO,CAAC,EAAE;AAKjD,MAAI;AAGF,UAAM,cAAc,6BAA6B,WAAW;AAC5D,QAAI,CAAC,YAAY,YAAY,YAAY,WAAW,MAAM;AACxD,oBAAc,sBAAsB,YAAY;AAChD,UAAO,eAAW,YAAY,MAAM,GAAG;AACrC,sBAAc,iCAAoC;AAAA,UAChD,YAAY;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,cAAc,MAAM,wBAAwB,aAAa;AAAA;AAAA;AAAA;AAAA,MAI7D,OAAO,QAAQ,UAAU,QAAQ,QAAQ;AAAA,IAC3C,CAAC;AAID,QAAI,YAAY,aAAa,QAAW;AACtC,oBAAc,sBAAsB,YAAY;AAAA,IAClD;AACA,UAAM,eACJ,YAAY,aAAa,SAChB,eAAS,aAAa,YAAY,QAAQ,IAC/C;AACN,YAAQ,YAAY,QAAQ;AAAA,MAC1B,KAAK;AACH,gBAAQ,KAAK,WAAW,YAAY,EAAE;AACtC,sBAAc,MAAM,KAAK,iBAAiB;AAC1C;AAAA,MACF,KAAK;AACH,gBAAQ,KAAK,0CAA0C,YAAY,EAAE;AACrE,sBAAc,MAAM,KAAK,iBAAiB;AAC1C;AAAA,MACF,KAAK;AACH,gBAAQ;AAAA,UACN,oDAAoD,YAAY;AAAA,QAClE;AACA,sBAAc,MAAM,KAAK,iBAAiB;AAC1C;AAAA,MACF,KAAK;AACH,gBAAQ,KAAK,WAAW,YAAY,uCAAuC;AAC3E;AAAA,MACF,KAAK;AAKH,iBAAS;AAAA,UACP,aAAa,YAAY;AAAA,QAC3B;AACA;AAAA,MACF,KAAK,YAAY;AAKf,cAAM,UACJ,YAAY,aAAa,SAChB,eAAS,aAAa,YAAY,QAAQ,IAC/C;AACN,cAAM,YACJ,YAAY,oBAAoB,SACvB,eAAS,aAAa,YAAY,eAAe,IACtD;AACN,cAAM,cAAc,cAAc,OAAO,aAAa,aAAa;AACnE,eAAO;AAAA,UACL,QAAQ,OAAO,QAAQ,SAAS;AAAA,kCACK,OAAO,eAAe,SAAS;AAAA,QACtE;AACA,eAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,MAClD;AAAA,MACA,KAAK;AACH,iBAAS;AAAA,UACP,GAAG,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAKjB;AACA;AAAA,IACJ;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,cAAc,cAAc,OAAO,aAAa,aAAa;AACnE,WAAO,KAAK,yCAAyC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AACvG,WAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,EAClD;AAGA,MAAI;AACF,UAAM,eAAe,MAAM,mBAAmB,WAAW;AACzD,QAAI,cAAc,UAAU;AAC1B,cAAQ,KAAK,iDAAiD;AAC9D,oBAAc,MAAM,KAAK,aAAa;AAAA,IACxC,WAAW,iBAAiB,MAAM;AAChC,eAAS,KAAK,0EAA0E;AAAA,IAC1F,WAAW,aAAa,WAAW,mBAAmB;AACpD,cAAQ,KAAK,6DAA6D;AAAA,IAC5E,WAAW,aAAa,WAAW,cAAc;AAC/C,eAAS,KAAK,mFAA8E;AAAA,IAC9F,OAAO;AACL,eAAS,KAAK,2FAAsF;AAAA,IACtG;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,cAAc,cAAc,OAAO,aAAa,aAAa;AACnE,WAAO,KAAK,iCAAiC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAC/F,WAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,EAClD;AAOA,MAAI;AACF,UAAM,kBAAuB,WAAK,aAAa,YAAY;AAC3D,QAAI,iBAAiB;AACrB,QAAO,eAAW,eAAe,GAAG;AAClC,YAAM,kBAAqB,iBAAa,iBAAiB,OAAO;AAChE,uBAAiB,YAAY,mBAAmB,eAAe,CAAC;AAAA,IAClE;AACA,UAAM,aAAa,MAAM,iBAAiB,WAAW;AACrD,QAAI,YAAY;AACd,cAAQ,KAAK,kDAAkD;AAC/D,oBAAc,MAAM,KAAK,WAAW;AAAA,IACtC,WAAW,gBAAgB;AACzB,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF,OAAO;AACL,cAAQ,KAAK,4DAA4D;AAAA,IAC3E;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,cAAc,cAAc,OAAO,aAAa,aAAa;AACnE,WAAO,KAAK,gCAAgC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAC9F,WAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,EAClD;AAGA,MAAI;AACF,UAAM,mBAAmB,MAAM,kBAAkB,WAAW;AAC5D,QAAI,kBAAkB;AACpB,cAAQ,KAAK,sCAAsC;AACnD,oBAAc,MAAM,KAAK,WAAW;AAAA,IACtC,OAAO;AACL,cAAQ,KAAK,kDAAkD;AAAA,IACjE;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,cAAc,cAAc,OAAO,aAAa,aAAa;AACnE,WAAO,KAAK,gCAAgC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAC9F,WAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,EAClD;AAQA,QAAM,QAAQ,QAAQ,IAAI,IAAI;AAC9B,QAAM,OACH,OAAO,UAAU,YAChB,MAAM,KAAK,MAAM,MACjB,MAAM,YAAY,MAAM,WACxB,MAAM,KAAK,MAAM,OACnB,QAAQ,IAAI,gBAAgB,MAAM;AAEpC,MAAI;AAOF,UAAM,qBAAqB,MAAM,YAAY,WAAW;AACxD,UAAM,UAAU,MAAM,mBAAmB,WAAW;AACpD,QAAI,uBAAuB,MAAM;AAC/B,cAAQ,KAAK,yCAAyC;AAAA,IACxD;AAYA,QAAI;AACF,YAAM,kBAAkB,mBAAmB,aAAa,OAAO;AAC/D,YAAM,UAAU,sBAAsB,gBAAgB,MAAM;AAC5D,cAAQ,gBAAgB,QAAQ;AAAA,QAC9B,KAAK;AACH,kBAAQ,KAAK,WAAW,OAAO,EAAE;AACjC,wBAAc,MAAM,KAAK,gBAAgB;AACzC;AAAA,QACF,KAAK;AACH,kBAAQ,KAAK,WAAW,OAAO,yBAAyB;AAMxD;AAAA,QACF,KAAK;AACH,kBAAQ,KAAK,WAAW,OAAO,6BAA6B;AAC5D;AAAA,QACF,KAAK;AACH,kBAAQ;AAAA,YACN,WAAW,OAAO;AAAA,UACpB;AAIA;AAAA,QACF,KAAK;AACH,mBAAS;AAAA,YACP,mBAAmB,OAAO,GACxB,gBAAgB,UAAU,SACtB,KAAK,gBAAgB,KAAK,KAC1B,EACN;AAAA,UACF;AACA;AAAA,MACJ;AAOA,YAAM,gBAAqB,WAAK,aAAa,YAAY;AACzD,UAAO,eAAW,aAAa,GAAG;AAChC,YAAI;AACF,gBAAM,mBAAsB,iBAAa,eAAe,OAAO;AAC/D,cAAI,+BAA+B,kBAAkB,gBAAgB,MAAM,GAAG;AAC5E,qBAAS;AAAA,cACP,4BAA4B,OAAO,8NAGjC,UACA;AAAA,YACJ;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,eAAS;AAAA,QACP,mBAAmB;AAAA,UACjB,kBAAkB,WAAW,EAAE;AAAA,QACjC,CAAC,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACxD;AAAA,IACF;AAeA,QAAI,mBAAmB;AACvB,QAAI,+BAA+B;AAQnC,UAAM,WAAW,MAAM,8BAA8B,WAAW;AAChE,UAAM,SAAS,SAAS,WAAW,OAAO;AAE1C,QAAI,MAAM;AAKR,YAAM,eAA8B;AAAA,QAClC,MAAM;AAAA,QACN,eAAoB,WAAK,aAAa,eAAe,UAAU;AAAA,QAC/D,cAAc;AAAA,QACd,cAAc;AAAA,QACd,qBAAqB;AAAA,MACvB;AACA,YAAM,gBAAgB,kBAAkB,cAAc,cAAc,MAAM;AAC1E,YAAM,WAAW,MAAM,sBAAsB;AAAA,QAC3C,YAAY,aAAa;AAAA,QACzB,iBAAiB;AAAA,QACjB,OAAO;AAAA,MACT,CAAC;AACD,UAAI,aAAa,QAAQ;AACvB,cAAM,eAAe,cAAc,eAAe,WAAW;AAC7D,YAAI,aAAa,kBAAkB,QAAW,eAAW,aAAa,aAAa,GAAG;AACpF,yCAA+B;AAAA,QACjC;AAAA,MACF;AACA,UAAI,aAAa,kBAAkB,QAAW,eAAW,aAAa,aAAa,GAAG;AACpF,2BAAmB;AACnB,gBAAQ,KAAK,wCAAwC;AAAA,MACvD;AAAA,IACF,OAAO;AAEL,UAAI;AACJ,UAAI;AACF,iBAAS,MAAM,aAAa,WAAW;AAAA,MACzC,SAAS,WAAW;AAClB,iBAAS;AAAA,UACP,2BAA2B,qBAAqB,QAAQ,UAAU,UAAU,OAAO,SAAS,CAAC;AAAA,QAC/F;AAEA,cAAM,eAA8B;AAAA,UAClC,MAAM;AAAA,UACN,eAAoB,WAAK,aAAa,eAAe,UAAU;AAAA,UAC/D,cAAc;AAAA,UACd,cAAc;AAAA,UACd,qBAAqB;AAAA,QACvB;AACA,cAAM,gBAAgB,kBAAkB,cAAc,cAAc,MAAM;AAC1E,cAAM,eAAe,cAAc,eAAe,WAAW;AAC7D,YAAI,aAAa,kBAAkB,QAAW,eAAW,aAAa,aAAa,GAAG;AACpF,6BAAmB;AACnB,yCAA+B;AAAA,QACjC;AACA,iBAAS,CAAC;AAAA,MACZ;AAEA,YAAM,kBAA4B,CAAC;AAEnC,iBAAW,SAAS,QAAQ;AAC1B,YAAI;AACF,gBAAM,gBAAgB,kBAAkB,OAAO,cAAc,MAAM;AAMnE,gBAAM,WAAW,MAAM,sBAAsB;AAAA,YAC3C,YAAY,MAAM;AAAA,YAClB,iBAAiB;AAAA,YACjB,OAAO,QAAQ,UAAU,QAAQ,QAAQ;AAAA,UAC3C,CAAC;AAED,cAAI,aAAa,QAAQ;AACvB,oBAAQ;AAAA,cACN,sBAAsB,MAAM,iBAAiB,MAAM,IAAI;AAAA,YACzD;AACA,gBAAI,MAAM,kBAAkB,QAAW,eAAW,MAAM,aAAa,GAAG;AAItE,iCAAmB;AAAA,YACrB;AACA;AAAA,UACF;AAEA,gBAAM,eAAe,OAAO,eAAe,WAAW;AAItD,gBAAM,eAAe,MAAM,kBAAkB,QAAW,eAAW,MAAM,aAAa;AACtF,cAAI,CAAC,cAAc;AACjB;AAAA,UACF;AAEA,6BAAmB;AACnB,yCAA+B;AAE/B,gBAAM,cAAc,oBAAoB,OAAO,YAAY;AAC3D,cAAI,gBAAgB,IAAI;AACtB,kBAAM,kBAAkB,OAAO,aAAa,WAAW;AAAA,UACzD;AAEA,cAAI,MAAM,SAAS,WAAW;AAC5B,4BAAgB,KAAK,gBAAgB,MAAM,IAAI,CAAC;AAAA,UAClD;AAAA,QACF,SAAS,UAAU;AACjB,mBAAS;AAAA,YACP,+BAA+B,MAAM,IAAI,KAAK,oBAAoB,QAAQ,SAAS,UAAU,OAAO,QAAQ,CAAC;AAAA,UAC/G;AAAA,QACF;AAAA,MACF;AAEA,UAAI,gBAAgB,SAAS,GAAG;AAC9B,gBAAQ,KAAK,uBAAuB,gBAAgB,KAAK,IAAI,CAAC,EAAE;AAAA,MAClE,WAAW,kBAAkB;AAC3B,gBAAQ,KAAK,+CAA+C;AAAA,MAC9D;AAAA,IACF;AAGA,UAAM;AAAA,MACJ,CAAC,aAAa,oBAAoB,yBAAyB,oBAAoB;AAAA,MAC/E;AAAA,IACF;AAYA,QAAI,8BAA8B;AAChC,YAAM,eAAe,SAAS,WAAW,UAAU;AACnD,YAAM,aAAa,oBAAoB,MAAM;AAC7C,YAAM,gBAAgB,MAAM,eAAe,aAAa;AAAA,QACtD,kBAAkB;AAAA,QAClB,gBAAgB;AAAA,MAClB,CAAC;AACD,UAAI,eAAe;AACjB,gBAAQ,KAAK,0CAA0C;AAAA,MACzD;AAAA,IACF;AAAA,EACF,SAAS,QAAQ;AACf,aAAS;AAAA,MACP,kCAAkC,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM,CAAC;AAAA,IAC7F;AAAA,EACF;AAGA,MAAI,oBAAoB;AACxB,MAAI,CAAC,OAAO,CAAC,aAAa;AACxB,QAAI,QAAQ,MAAM,OAAO;AACvB,0BAAoB,MAAM;AAAA,QACxB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,mBAAmB;AACrB,QAAI;AACF,YAAM,QAAQ,MAAM,kBAAkB,WAAW;AACjD,UAAI,OAAO;AACT,gBAAQ,KAAK,kDAAkD;AAAA,MACjE;AAAA,IACF,SAAS,KAAK;AACZ,eAAS,KAAK,mCAAmC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,IACrG;AAGA,QAAI;AACF,YAAM,iBAAiB,WAAW;AAClC,cAAQ,KAAK,qCAAqC;AAAA,IACpD,SAAS,KAAK;AACZ,eAAS,KAAK,6BAA6B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,yBAAyB;AAAA,IACtH;AAAA,EACF;AAaA,QAAM,aACJ,QAAQ,IAAI,6BAA6B,MAAM,OAC/C,QAAQ,IAAI,6BAA6B,MAAM,UAC/C,QAAQ,IAAI,QAAQ,MAAM;AAE5B,MAAI,CAAC,cAAc,CAAC,MAAM;AACxB,UAAM,eAAe,MAAM,0BAA0B,WAAW;AAChE,QAAI,aAAa,YAAY,UAAU;AACrC,aAAO,KAAK,aAAa,KAAK;AAC9B,aAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAAA,IAClD;AACA,QAAI,aAAa,YAAY,YAAY;AACvC,cAAQ,KAAK,oDAAoD;AAAA,IACnE,OAAO;AAKL,cAAQ,KAAK,qDAAqD;AAAA,IACpE;AAAA,EACF;AAEA,SAAO,EAAE,UAAU,GAAG,SAAS,UAAU,OAAO;AAClD;AA0CA,eAAsB,0BACpB,aAC+B;AAI/B,QAAM,UAAU,MAAM,YAAY,WAAW;AAC7C,MAAI,YAAY,MAAM;AAKpB,WAAO,EAAE,SAAS,UAAU;AAAA,EAC9B;AAaA,MAAI;AACJ,MAAI;AACF,UAAM,UAAe,WAAK,aAAa,YAAY;AACnD,QAAO,eAAW,OAAO,GAAG;AAC1B,YAAM,aAAgB,iBAAa,SAAS,OAAO;AACnD,YAAM,YAAY,mBAAmB,UAAU;AAG/C,UAAI,cAAc,QAAQ,YAAY,SAAS,GAAG;AAChD,iBAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAUA,QAAM,aAAa,cAAc,EAAE,QAAQ,OAAO,CAAC;AACnD,QAAM,SAAS,EAAE,GAAG,YAAY,QAAQ,OAAO;AAC/C,QAAM,aAAa,OAAsC,UAAkB;AAE3E,QAAM,SAA2B,MAAM,oBAAoB,QAAQ,SAAS,UAAU;AAEtF,MAAI,OAAO,IAAI;AACb,WAAO,EAAE,SAAS,WAAW;AAAA,EAC/B;AAEA,QAAM,OAAO;AACb,UAAQ,OAAO,QAAQ;AAAA,IACrB,KAAK;AAKH,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,sDAAsD,OAAO,MAAM,KAAK,IAAI;AAAA,MACrF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,sEAAsE,OAAO,MAAM,MAAM,IAAI;AAAA,MACtG;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,4EAA4E,IAAI;AAAA,MACzF;AAAA,EACJ;AACF;AAKA,SAAS,UAAU,MAA6B;AAC9C,QAAM,OAAO,KAAK,MAAM,CAAC;AACzB,MAAI,MAAM;AACV,MAAI,cAAc;AAClB,MAAI,QAAQ;AAEZ,aAAW,OAAO,MAAM;AACtB,QAAI,QAAQ,WAAW,QAAQ,MAAM;AACnC,YAAM;AAAA,IACR,WAAW,QAAQ,kBAAkB;AACnC,oBAAc;AAAA,IAChB,WAAW,QAAQ,WAAW;AAC5B,cAAQ;AAAA,IACV;AAAA,EACF;AAGA,MAAI,CAAC,QAAQ,MAAM,OAAO;AACxB,UAAM;AAAA,EACR;AAEA,SAAO;AAAA,IACL,aAAa,QAAQ,IAAI;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAMA,IAAM,aACJ,OAAO,YAAY,eAAe,QAAQ,KAAK,CAAC,MAAM,SAClD,QAAQ,KAAK,CAAC,EAAE,QAAQ,OAAO,GAAG,IAClC;AAEN,IAAM,iBAAiB,eAAe,SAAiB,eAAS,UAAU,IAAI;AAE9E,IAAM,oBACJ,eAAe,WACd,WAAW,SAAS,cAAc,KACjC,WAAW,SAAS,cAAc,KAClC,mBAAmB;AAEvB,IAAI,mBAAmB;AAIrB,MAAI,CAAC,iBAAiB,EAAE,GAAG;AACzB,YAAQ,OAAO;AAAA,MACb,mEAAmE,QAAQ,OAAO;AAAA;AAAA,IACpF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,aAAa,QAAQ,KAAK,CAAC;AAEjC,MAAI,eAAe,OAAO;AACxB,QAAI,QAAQ,KAAK,CAAC,MAAM,OAAO;AAE7B,YAAM,gBAAgB,QAAQ,KAAK,MAAM,CAAC;AAC1C,YAAM,QAAQ,cAAc,SAAS,SAAS;AAC9C,YAAM,SAAS,cAAc,SAAS,WAAW;AAEjD,aAAO,cAAc,EAClB,KAAK,CAAC,EAAE,OAAO,MAAM,OAAO,EAAE,OAAO,OAAO,CAAC,CAAC,EAC9C,KAAK,CAAC,WAAW;AAChB,mBAAW,OAAO,OAAO,UAAU;AACjC,kBAAQ,OAAO,MAAM,MAAM,IAAI;AAAA,QACjC;AACA,gBAAQ,KAAK,OAAO,QAAQ;AAAA,MAC9B,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,gBAAQ,OAAO;AAAA,UACb,gBAAgB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,QAClE;AACA,gBAAQ,KAAK,CAAC;AAAA,MAChB,CAAC;AAAA,IACL,OAAO;AACL,cAAQ,OAAO;AAAA,QACb,2BAA2B,QAAQ,KAAK,CAAC,KAAK,QAAQ;AAAA;AAAA;AAAA;AAAA,MAExD;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,WAAW,eAAe,UAAa,eAAe,UAAU,WAAW,WAAW,GAAG,GAAG;AAE1F,UAAM,gBAAgB,QAAQ,KAAK,MAAM,eAAe,SAAS,IAAI,CAAC;AAUtE,QAAI,cAAc,SAAS,YAAY,GAAG;AACxC,UAAI,sBAAsB,QAAQ,IAAI;AACtC,UAAI;AACF,8BAAsB,mBAAmB,mBAAmB,EAAE;AAAA,MAChE,QAAQ;AAAA,MAIR;AACA,aAAO,eAAe,EACnB,KAAK,CAAC,EAAE,YAAY,MAAM,YAAY,EAAE,aAAa,oBAAoB,CAAC,CAAC,EAC3E,KAAK,CAAC,WAAW;AAChB,mBAAW,QAAQ,OAAO,SAAS;AACjC,kBAAQ,OAAO,MAAM,GAAG,IAAI;AAAA,CAAI;AAAA,QAClC;AACA,mBAAW,SAAS,OAAO,QAAQ;AACjC,kBAAQ,OAAO,MAAM,OAAO,MAAM,OAAO;AAAA,CAAI;AAC7C,cAAI,MAAM,KAAK;AACb,oBAAQ,OAAO,MAAM,cAAc,MAAM,GAAG;AAAA,CAAI;AAAA,UAClD;AAAA,QACF;AACA,gBAAQ,KAAK,OAAO,QAAQ;AAAA,MAC9B,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,gBAAQ,OAAO;AAAA,UACb,gBAAgB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,QAClE;AACA,gBAAQ,KAAK,CAAC;AAAA,MAChB,CAAC;AAAA,IACL,OAAO;AACL,YAAM,UAAU,UAAU,QAAQ,IAAI;AAEtC,cAAQ,OAAO,EACZ,KAAK,CAAC,WAAW;AAChB,YAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,qBAAW,OAAO,OAAO,QAAQ;AAC/B,oBAAQ,OAAO,MAAM,UAAU,GAAG;AAAA,CAAI;AAAA,UACxC;AAAA,QACF;AACA,YAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,qBAAW,QAAQ,OAAO,UAAU;AAClC,oBAAQ,OAAO,MAAM,YAAY,IAAI;AAAA,CAAI;AAAA,UAC3C;AAAA,QACF;AACA,YAAI,OAAO,QAAQ,SAAS,GAAG;AAK7B,cAAI,OAAO,aAAa,GAAG;AACzB,oBAAQ,OAAO,MAAM,4CAA4C;AAAA,UACnE,OAAO;AACL,oBAAQ,OAAO,MAAM,8CAA8C;AAAA,UACrE;AACA,qBAAW,QAAQ,OAAO,SAAS;AACjC,oBAAQ,OAAO,MAAM,OAAO,IAAI;AAAA,CAAI;AAAA,UACtC;AACA,cAAI,OAAO,aAAa,GAAG;AACzB,oBAAQ,OAAO,MAAM,iBAAiB;AACtC,oBAAQ,OAAO,MAAM,sCAAsC;AAC3D,oBAAQ,OAAO;AAAA,cACb;AAAA,YACF;AACA,oBAAQ,OAAO;AAAA,cACb;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,gBAAQ,KAAK,OAAO,QAAQ;AAAA,MAC9B,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,gBAAQ,OAAO;AAAA,UACb,gBAAgB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,QAClE;AACA,gBAAQ,KAAK,CAAC;AAAA,MAChB,CAAC;AAAA,IACL;AAAA,EACF,WAAW,eAAe,UAAU;AAClC,UAAM,gBAAgB,QAAQ,KAAK,MAAM,CAAC;AAC1C,UAAM,SAAS,cAAc,SAAS,WAAW;AACjD,UAAM,QAAQ,cAAc,SAAS,SAAS;AAE9C,WAAO,aAAa,EACjB,KAAK,CAAC,EAAE,UAAU,MAAM,UAAU,EAAE,aAAa,QAAQ,IAAI,GAAG,QAAQ,MAAM,CAAC,CAAC,EAChF,KAAK,CAAC,WAAW;AAChB,UAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,mBAAW,OAAO,OAAO,QAAQ;AAC/B,kBAAQ,OAAO,MAAM,UAAU,GAAG;AAAA,CAAI;AAAA,QACxC;AAAA,MACF;AACA,UAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,mBAAW,QAAQ,OAAO,UAAU;AAClC,kBAAQ,OAAO,MAAM,YAAY,IAAI;AAAA,CAAI;AAAA,QAC3C;AAAA,MACF;AACA,UAAI,OAAO,QAAQ,SAAS,GAAG;AAC7B,gBAAQ,OAAO,MAAM,IAAI;AACzB,mBAAW,QAAQ,OAAO,SAAS;AACjC,kBAAQ,OAAO,MAAM,KAAK,IAAI;AAAA,CAAI;AAAA,QACpC;AACA,gBAAQ,OAAO,MAAM,IAAI;AAAA,MAC3B;AACA,cAAQ,KAAK,OAAO,QAAQ;AAAA,IAC9B,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,cAAQ,OAAO;AAAA,QACb,gBAAgB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,MAClE;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC;AAAA,EACL,WAAW,eAAe,UAAU;AAClC,UAAM,gBAAgB,QAAQ,KAAK,MAAM,CAAC;AAC1C,UAAM,OAAO,cAAc,SAAS,QAAQ;AAE5C,YAAQ,IAAI,CAAC,OAAO,aAAa,GAAG,OAAO,yBAAe,CAAC,CAAC,EACzD,KAAK,CAAC,CAAC,EAAE,UAAU,GAAG,EAAE,oBAAoB,QAAQ,CAAC,MAAM;AAC1D,UAAI,cAAc,QAAQ,IAAI;AAC9B,UAAI;AACF,sBAAc,QAAQ,WAAW,EAAE;AAAA,MACrC,QAAQ;AAAA,MAER;AACA,YAAM,SAAS,UAAU,EAAE,YAAY,CAAC;AACxC,UAAI,MAAM;AACR,gBAAQ,OAAO,MAAM,KAAK,UAAU,MAAM,IAAI,IAAI;AAAA,MACpD,OAAO;AACL,cAAM,SAAS;AAAA,UACb,CAAC,aAAa,OAAO,SAAS;AAAA,UAC9B,CAAC,eAAe,OAAO,WAAW;AAAA,UAClC,CAAC,mBAAmB,OAAO,eAAe;AAAA,UAC1C,CAAC,kBAAkB,OAAO,aAAa;AAAA,UACvC,CAAC,YAAY,OAAO,OAAO;AAAA,UAC3B,CAAC,kBAAkB,OAAO,aAAa;AAAA,QACzC;AACA,mBAAW,CAAC,OAAO,EAAE,KAAK,QAAQ;AAChC,kBAAQ,OAAO,MAAM,KAAK,KAAK,MAAM,GAAG,IAAI,KAAK;AAAA,CAAI;AAAA,QACvD;AACA,YAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,kBAAQ,OAAO,MAAM,eAAe,OAAO,OAAO,KAAK,IAAI,CAAC;AAAA,CAAI;AAAA,QAClE,OAAO;AACL,kBAAQ,OAAO,MAAM,cAAc;AAAA,QACrC;AAAA,MACF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,cAAQ,OAAO;AAAA,QACb,gBAAgB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,MAClE;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC;AAAA,EACL,OAAO;AACL,YAAQ,OAAO;AAAA,MACb,oBAAoB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMhC;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":["fs","path","readline","wrapResult","modified"]}
@@ -14710,7 +14710,7 @@ function formatAgentName(name) {
14710
14710
  gemini: "Gemini",
14711
14711
  cursor: "Cursor",
14712
14712
  windsurf: "Windsurf",
14713
- generic: "Generic"
14713
+ generic: "Generic helper"
14714
14714
  };
14715
14715
  return displayNames[name];
14716
14716
  }
@@ -14825,7 +14825,8 @@ async function mcpAdd(options) {
14825
14825
  }
14826
14826
  const agents = await detectAgents(projectRoot);
14827
14827
  const detectedNonGeneric = agents.filter((a) => a.name !== "generic");
14828
- const targetAgents = detectedNonGeneric.length > 0 ? detectedNonGeneric : agents.filter((a) => a.name === "generic");
14828
+ const genericAgent = agents.find((a) => a.name === "generic");
14829
+ const targetAgents = genericAgent ? [...detectedNonGeneric, genericAgent] : detectedNonGeneric;
14829
14830
  if (dryRun) {
14830
14831
  messages.push("Dry run: would perform the following actions:", "");
14831
14832
  for (const agent of targetAgents) {
@@ -14934,6 +14935,17 @@ async function mcpAdd(options) {
14934
14935
  " No agents detected. Place agent marker files (e.g., CLAUDE.md, .cursor/) in your project."
14935
14936
  );
14936
14937
  }
14938
+ const detectedNonGenericResults = results.filter(
14939
+ (r) => detectedNonGeneric.some((a) => a.name === r.agent)
14940
+ );
14941
+ const allDetectedNonGenericFailed = detectedNonGeneric.length > 0 && !detectedNonGenericResults.some((r) => r.success);
14942
+ if (allDetectedNonGenericFailed) {
14943
+ messages.push(
14944
+ "",
14945
+ "All detected agent registrations failed. Check errors above."
14946
+ );
14947
+ return { exitCode: 1, results, messages };
14948
+ }
14937
14949
  if (!anySuccess && results.length > 0) {
14938
14950
  messages.push(
14939
14951
  "",