{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { dirname, join, resolve } from \"path\";\nimport { fileURLToPath } from \"url\";\n\n// =============================================================================\n// Package Detection\n// =============================================================================\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Detect if we're running as a Bun compiled binary.\n * Bun binaries have import.meta.url containing \"$bunfs\", \"~BUN\", or \"%7EBUN\" (Bun's virtual filesystem path)\n */\nexport const isBunBinary =\n\timport.meta.url.includes(\"$bunfs\") || import.meta.url.includes(\"~BUN\") || import.meta.url.includes(\"%7EBUN\");\n\n/** Detect if Bun is the runtime (compiled binary or bun run) */\nexport const isBunRuntime = !!process.versions.bun;\n\n/**\n * Get the base directory for resolving package assets (themes, package.json, README.md, CHANGELOG.md).\n * Note: Does NOT use environment variables to avoid circular dependency during APP_NAME detection.\n */\nfunction findPackageDir(): string {\n\tif (isBunBinary) {\n\t\treturn dirname(process.execPath);\n\t}\n\t// Node.js: walk up from __dirname until we find package.json\n\tlet dir = __dirname;\n\twhile (dir !== dirname(dir)) {\n\t\tif (existsSync(join(dir, \"package.json\"))) {\n\t\t\treturn dir;\n\t\t}\n\t\tdir = dirname(dir);\n\t}\n\treturn __dirname;\n}\n\nconst PACKAGE_DIR = findPackageDir();\nconst pkg = JSON.parse(readFileSync(join(PACKAGE_DIR, \"package.json\"), \"utf-8\"));\n\nexport const APP_NAME: string = pkg.jensenConfig?.name || pkg.piConfig?.name || \"jensen\";\nexport const CONFIG_DIR_NAME: string = pkg.jensenConfig?.configDir || pkg.piConfig?.configDir || \".jensen\";\nexport const VERSION: string = pkg.version;\nexport const PACKAGE_NAME: string = pkg.name;\nexport const FILE_PREFIX: string = APP_NAME.toLowerCase().replace(/\\s+/g, \"-\");\nexport const ENV_PREFIX: string = APP_NAME.toUpperCase().replace(/\\s+/g, \"_\");\n\n// =============================================================================\n// Development Mode Detection\n// =============================================================================\n\n/**\n * Detect if we're running from source (development mode) rather than an installed package.\n * True when the package directory contains a `src/` directory (i.e., a development checkout).\n */\nexport function isDevMode(): boolean {\n\treturn existsSync(join(PACKAGE_DIR, \"src\"));\n}\n\n// =============================================================================\n// Version Comparison\n// =============================================================================\n\n/** Strict stable semver core: major.minor.patch with no leading zeros. */\nconst STRICT_SEMVER_RE = /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$/;\n\nexport interface StrictVersion {\n\tmajor: number;\n\tminor: number;\n\tpatch: number;\n}\n\n/**\n * Parse a strict stable semver core string.\n *\n * Accepts only the exact form major.minor.patch where each component is\n * either the single digit 0 or a positive integer with no leading zero.\n *\n * Returns null for anything else, including:\n *   - missing components (\"2\", \"1.1\")\n *   - extra components (\"1.1.8.1\")\n *   - leading zeros (\"01.1.9\")\n *   - prefixes / suffixes (\"v1.1.8\", \"1.1.8-beta.1\", \"1.1.8+build.1\")\n *   - whitespace, non-string types\n */\nexport function parseStrictStableVersion(version: string): StrictVersion | null {\n\tif (typeof version !== \"string\") return null;\n\tconst match = version.match(STRICT_SEMVER_RE);\n\tif (!match) return null;\n\treturn {\n\t\tmajor: parseInt(match[1], 10),\n\t\tminor: parseInt(match[2], 10),\n\t\tpatch: parseInt(match[3], 10),\n\t};\n}\n\n/**\n * Compare two version strings using strict stable semver ordering.\n * Returns true if a > b.\n *\n * Malformed versions (anything other than major.minor.patch) are\n * rejected and comparison returns false — no update is shown.\n */\nexport function semverGt(a: string, b: string): boolean {\n\tconst parsedA = parseStrictStableVersion(a);\n\tconst parsedB = parseStrictStableVersion(b);\n\tif (!parsedA || !parsedB) return false;\n\tif (parsedA.major !== parsedB.major) return parsedA.major > parsedB.major;\n\tif (parsedA.minor !== parsedB.minor) return parsedA.minor > parsedB.minor;\n\treturn parsedA.patch > parsedB.patch;\n}\n\n// =============================================================================\n// Release Channel\n// =============================================================================\n\n/** Supported npm dist-tag channels for the Apholdings Jensen Code fork. */\nexport const VALID_RELEASE_CHANNELS = [\"fork\", \"latest\"] as const;\nexport type ReleaseChannel = (typeof VALID_RELEASE_CHANNELS)[number];\n\n/**\n * Get the release channel from environment variable.\n *\n * Uses own-property presence checks, not truthiness, to distinguish\n * \"absent\" from \"explicitly set to empty string\".  An explicitly set\n * empty variable is invalid configuration and must fail closed.\n *\n * Returns undefined when neither environment variable is present.\n */\nexport function getReleaseChannelEnv(): string | undefined {\n\tconst jensenKey = `${ENV_PREFIX}_RELEASE_CHANNEL`;\n\tif (jensenKey in process.env) {\n\t\treturn process.env[jensenKey];\n\t}\n\tif (\"PI_RELEASE_CHANNEL\" in process.env) {\n\t\treturn process.env.PI_RELEASE_CHANNEL;\n\t}\n\treturn undefined;\n}\n\n/**\n * Resolve a raw release channel value to a valid ReleaseChannel or undefined.\n *\n * When explicitValue is undefined (neither env nor persisted setting is set),\n * this Apholdings fork defaults to \"fork\".\n *\n * An explicit but invalid value returns undefined (fail-closed).  The caller\n * must not fall through to another channel source.\n */\nexport function resolveReleaseChannel(explicitValue: string | undefined): ReleaseChannel | undefined {\n\tif (explicitValue === undefined) return \"fork\";\n\tif ((VALID_RELEASE_CHANNELS as readonly string[]).includes(explicitValue)) {\n\t\treturn explicitValue as ReleaseChannel;\n\t}\n\treturn undefined;\n}\n\n// =============================================================================\n// Install Method Detection\n// =============================================================================\n\nexport type InstallMethod = \"bun-binary\" | \"npm\" | \"pnpm\" | \"yarn\" | \"bun\" | \"unknown\";\n\nexport function detectInstallMethod(): InstallMethod {\n\tif (isBunBinary) {\n\t\treturn \"bun-binary\";\n\t}\n\n\tconst resolvedPath = `${__dirname}\\0${process.execPath || \"\"}`.toLowerCase();\n\n\tif (resolvedPath.includes(\"/pnpm/\") || resolvedPath.includes(\"/.pnpm/\") || resolvedPath.includes(\"\\\\pnpm\\\\\")) {\n\t\treturn \"pnpm\";\n\t}\n\tif (resolvedPath.includes(\"/yarn/\") || resolvedPath.includes(\"/.yarn/\") || resolvedPath.includes(\"\\\\yarn\\\\\")) {\n\t\treturn \"yarn\";\n\t}\n\tif (isBunRuntime) {\n\t\treturn \"bun\";\n\t}\n\tif (resolvedPath.includes(\"/npm/\") || resolvedPath.includes(\"/node_modules/\") || resolvedPath.includes(\"\\\\npm\\\\\")) {\n\t\treturn \"npm\";\n\t}\n\n\treturn \"unknown\";\n}\n\nexport function getUpdateInstruction(packageName: string, channel?: ReleaseChannel): string {\n\tconst method = detectInstallMethod();\n\tconst tag = channel && channel !== \"latest\" ? `@${channel}` : \"\";\n\tswitch (method) {\n\t\tcase \"bun-binary\":\n\t\t\treturn `Download from: https://github.com/apholdings/jensen-code/releases/latest`;\n\t\tcase \"pnpm\":\n\t\t\treturn `Run: pnpm install -g ${packageName}${tag}`;\n\t\tcase \"yarn\":\n\t\t\treturn `Run: yarn global add ${packageName}${tag}`;\n\t\tcase \"bun\":\n\t\t\treturn `Run: bun install -g ${packageName}${tag}`;\n\t\tcase \"npm\":\n\t\t\treturn `Run: npm install -g ${packageName}${tag}`;\n\t\tdefault:\n\t\t\treturn `Run: npm install -g ${packageName}${tag}`;\n\t}\n}\n\n// =============================================================================\n// Package Asset Paths (shipped with executable)\n// =============================================================================\n\n/**\n * Get the base directory for resolving package assets (themes, package.json, README.md, CHANGELOG.md).\n * - For Bun binary: returns the directory containing the executable\n * - For Node.js (dist/): returns __dirname (the dist/ directory)\n * - For tsx (src/): returns parent directory (the package root)\n */\nexport function getPackageDir(): string {\n\t// Allow override via environment variable (useful for Nix/Guix where store paths tokenize poorly)\n\tconst envDir = process.env[`${ENV_PREFIX}_PACKAGE_DIR`] || process.env.PI_PACKAGE_DIR;\n\tif (envDir) {\n\t\tif (envDir === \"~\") return homedir();\n\t\tif (envDir.startsWith(\"~/\")) return homedir() + envDir.slice(1);\n\t\treturn envDir;\n\t}\n\treturn PACKAGE_DIR;\n}\n\n/**\n * Get path to built-in themes directory (shipped with package)\n * - For Bun binary: theme/ next to executable\n * - For Node.js (dist/): dist/modes/interactive/theme/\n * - For tsx (src/): src/modes/interactive/theme/\n */\nexport function getThemesDir(): string {\n\tif (isBunBinary) {\n\t\treturn join(dirname(process.execPath), \"theme\");\n\t}\n\t// Theme is in modes/interactive/theme/ relative to src/ or dist/\n\tconst packageDir = getPackageDir();\n\tconst srcOrDist = existsSync(join(packageDir, \"src\")) ? \"src\" : \"dist\";\n\treturn join(packageDir, srcOrDist, \"modes\", \"interactive\", \"theme\");\n}\n\n/**\n * Get path to HTML export template directory (shipped with package)\n * - For Bun binary: export-html/ next to executable\n * - For Node.js (dist/): dist/core/export-html/\n * - For tsx (src/): src/core/export-html/\n */\nexport function getExportTemplateDir(): string {\n\tif (isBunBinary) {\n\t\treturn join(dirname(process.execPath), \"export-html\");\n\t}\n\tconst packageDir = getPackageDir();\n\tconst srcOrDist = existsSync(join(packageDir, \"src\")) ? \"src\" : \"dist\";\n\treturn join(packageDir, srcOrDist, \"core\", \"export-html\");\n}\n\n/** Get path to package.json */\nexport function getPackageJsonPath(): string {\n\treturn join(getPackageDir(), \"package.json\");\n}\n\n/** Get path to README.md */\nexport function getReadmePath(): string {\n\treturn resolve(join(getPackageDir(), \"README.md\"));\n}\n\n/** Get path to docs directory */\nexport function getDocsPath(): string {\n\treturn resolve(join(getPackageDir(), \"docs\"));\n}\n\n/** Get path to examples directory */\nexport function getExamplesPath(): string {\n\treturn resolve(join(getPackageDir(), \"examples\"));\n}\n\n/** Get path to CHANGELOG.md */\nexport function getChangelogPath(): string {\n\treturn resolve(join(getPackageDir(), \"CHANGELOG.md\"));\n}\n\n// =============================================================================\n// App Config\n// =============================================================================\n\n// e.g., JENSEN_CODE_CODING_AGENT_DIR or PI_CODING_AGENT_DIR\nexport const ENV_AGENT_DIR = `${ENV_PREFIX}_CODING_AGENT_DIR`;\n\nconst DEFAULT_SHARE_VIEWER_URL = \"https://jensen.dev/session/\";\n\n/** Get the share viewer URL for a gist ID */\nexport function getShareViewerUrl(gistId: string): string {\n\tconst baseUrl =\n\t\tprocess.env[`${ENV_PREFIX}_SHARE_VIEWER_URL`] || process.env.PI_SHARE_VIEWER_URL || DEFAULT_SHARE_VIEWER_URL;\n\treturn `${baseUrl}#${gistId}`;\n}\n\n/** Get if offline mode is enabled via environment variable */\nexport function getOfflineEnv(): string | undefined {\n\treturn process.env[`${ENV_PREFIX}_OFFLINE`] || process.env.PI_OFFLINE;\n}\n\n/** Get if version check should be skipped via environment variable */\nexport function getSkipVersionCheckEnv(): string | undefined {\n\treturn process.env[`${ENV_PREFIX}_SKIP_VERSION_CHECK`] || process.env.PI_SKIP_VERSION_CHECK;\n}\n\n/** Get if timings should be enabled via environment variable */\nexport function getTimingEnv(): string | undefined {\n\treturn process.env[`${ENV_PREFIX}_TIMING`] || process.env.PI_TIMING;\n}\n\n/** Get if hardware cursor should be shown via environment variable */\nexport function getHardwareCursorEnv(): string | undefined {\n\treturn process.env[`${ENV_PREFIX}_HARDWARE_CURSOR`] || process.env.PI_HARDWARE_CURSOR;\n}\n\n/** Get if clear on shrink is enabled via environment variable */\nexport function getClearOnShrinkEnv(): string | undefined {\n\treturn process.env[`${ENV_PREFIX}_CLEAR_ON_SHRINK`] || process.env.PI_CLEAR_ON_SHRINK;\n}\n\n// =============================================================================\n// User Config Paths (~/.jensen/agent/*)\n// =============================================================================\n\n/** Get the agent config directory (e.g., ~/.jensen/agent/) */\nexport function getAgentDir(): string {\n\tconst envDir = process.env[ENV_AGENT_DIR] || process.env.PI_CODING_AGENT_DIR;\n\tif (envDir) {\n\t\t// Expand tilde to home directory\n\t\tif (envDir === \"~\") return homedir();\n\t\tif (envDir.startsWith(\"~/\")) return homedir() + envDir.slice(1);\n\t\treturn envDir;\n\t}\n\treturn join(homedir(), CONFIG_DIR_NAME, \"agent\");\n}\n\n/** Get path to user's custom themes directory */\nexport function getCustomThemesDir(): string {\n\treturn join(getAgentDir(), \"themes\");\n}\n\n/** Get path to models.json */\nexport function getModelsPath(): string {\n\treturn join(getAgentDir(), \"models.json\");\n}\n\n/** Get path to auth.json */\nexport function getAuthPath(): string {\n\treturn join(getAgentDir(), \"auth.json\");\n}\n\n/** Get path to settings.json */\nexport function getSettingsPath(): string {\n\treturn join(getAgentDir(), \"settings.json\");\n}\n\n/** Get path to tools directory */\nexport function getToolsDir(): string {\n\treturn join(getAgentDir(), \"tools\");\n}\n\n/** Get path to managed binaries directory (fd, rg) */\nexport function getBinDir(): string {\n\treturn join(getAgentDir(), \"bin\");\n}\n\n/** Get path to prompt templates directory */\nexport function getPromptsDir(): string {\n\treturn join(getAgentDir(), \"prompts\");\n}\n\n/** Get path to sessions directory */\nexport function getSessionsDir(): string {\n\treturn join(getAgentDir(), \"sessions\");\n}\n\n/** Get path to debug log file */\nexport function getDebugLogPath(): string {\n\treturn join(getAgentDir(), `${APP_NAME}-debug.log`);\n}\n"]}