{"version":3,"file":"matrixOptions.mjs","names":[],"sources":["../../src/cli/matrixOptions.ts"],"sourcesContent":["import path from 'node:path';\n\n/**\n * Matrix selection shared by the Pi and compatibility parsers.\n *\n * Both frontends pick the same three axes (profile, domains, major mode) out of\n * the same environment variables with the same defaults. They used to hold two\n * copies of that logic, and the copies drifted: the inline `--name=value` forms\n * and the strict value reader existed in the compatibility parser only, so\n * `./pi.sh --profile=x` silently forwarded an unknown flag to Pi. One copy here\n * is what keeps them honest.\n */\n\n/**\n * One variable per axis.\n *\n * These used to come in pairs, `DOOM_PI_*` for what the caller selected and\n * `DOOMPI_*` for what the harness resolved and published, with the second\n * outranking the first. A nested run still inherits its launcher's choice under\n * one name, because the launcher projects into the child environment and\n * overwrites the same key. Two names for one value only ever meant two ways to\n * disagree.\n */\nexport const DOOMPI_PROFILE_ENV = 'DOOMPI_PROFILE';\nexport const DOOMPI_DOMAINS_ENV = 'DOOMPI_DOMAINS';\nexport const DOOMPI_MAJOR_MODE_ENV = 'DOOMPI_MAJOR_MODE';\nexport const DOOMPI_ADDITIONAL_DIRECTORIES_ENV = 'DOOMPI_ADDITIONAL_DIRS';\nexport const DOOMPI_PRESET_ENV = 'DOOMPI_PRESET';\n\n/** Removed spelling of the major mode axis, reported rather than honored. */\nexport const REMOVED_DOOMPI_LAYER_ENV = 'DOOMPI_LAYER';\n\n/**\n * The whole retired namespace, reported rather than ignored.\n *\n * A prefix scan rather than a constant per variable: there were 28 of them, and\n * a list maintained by hand is a list that misses one. A stale\n * `export AGENT_HARNESS_MAJOR_MODE=dev` in a shell profile would otherwise\n * silently start the session on `copilot`.\n */\nconst RETIRED_ENV_PREFIX = 'AGENT_HARNESS_';\nconst CURRENT_ENV_PREFIX = 'DOOMPI_';\n\nexport const PROFILE_OPTION = '--profile';\nexport const DOMAIN_OPTION = '--domain';\nexport const DOMAINS_OPTION = '--domains';\n/**\n * The major mode flag.\n *\n * Not `--mode`: Pi owns that one for its output mode, and for a value outside\n * `text|json|rpc` it consumes both tokens and ignores them without a\n * diagnostic. A synced `pi --mode dev` would have failed silently.\n */\nexport const MAJOR_MODE_OPTION = '--major-mode';\nexport const ADD_DIRECTORY_OPTION = '--add-dir';\nexport const REMOVED_LAYER_OPTION = '--layer';\nexport const REMOVED_LAYERS_OPTION = '--layers';\nexport const REMOVED_TARGET_OPTION = '--target';\n\nexport const DEFAULT_MAJOR_MODE = 'copilot';\nexport const DEFAULT_DOMAIN = 'default';\n\nconst CSV_DELIMITER = ',';\nconst OPTION_PREFIX = '--';\n\nexport interface OptionMatch {\n  value: string;\n  /** Index of the last argument consumed, for the caller's loop cursor. */\n  nextIndex: number;\n  /** True for `--name=value`. Callers that re-emit an option preserve the form. */\n  inline: boolean;\n}\n\n/**\n * Reads `--name value` or `--name=value`, returning undefined for anything else.\n *\n * A value starting with `--` is rejected rather than consumed, so\n * `--profile --explain` reports a missing value instead of swallowing the next\n * flag. In compatibility mode this is also what stops the `--` provider\n * delimiter being taken as a matrix value.\n */\nexport function readOption(args: string[], index: number, name: string): OptionMatch | undefined {\n  // Callers walk `args` by index, so this is always in range.\n  const arg = args[index]!;\n  if (arg === name) {\n    const value = args[index + 1];\n    if (!value || value.startsWith(OPTION_PREFIX)) throw new Error(`${name} requires a value`);\n    return { value, nextIndex: index + 1, inline: false };\n  }\n  const inlinePrefix = `${name}=`;\n  if (!arg.startsWith(inlinePrefix)) return undefined;\n  const value = arg.slice(inlinePrefix.length);\n  if (!value) throw new Error(`${name} requires a value`);\n  return { value, nextIndex: index, inline: true };\n}\n\n/** Matches `--name` or `--name=value`, for reporting options that were removed. */\nexport function matchesOption(arg: string, name: string): boolean {\n  return arg === name || arg.startsWith(`${name}=`);\n}\n\nexport function parseCsv(value: string): string[] {\n  return value\n    .split(CSV_DELIMITER)\n    .map((item) => item.trim())\n    .filter(Boolean);\n}\n\nexport function parseRequiredCsv(value: string, option: string): string[] {\n  const values = parseCsv(value);\n  if (values.length === 0) throw new Error(`${option} requires a value`);\n  return values;\n}\n\nexport function parseMajorMode(value: string, source: string): string {\n  const majorMode = value.trim();\n  if (!majorMode) throw new Error(`${source} requires a value`);\n  if (majorMode.includes(CSV_DELIMITER)) throw new Error(`${source} accepts one major mode name`);\n  return majorMode;\n}\n\nexport function parseProfileValue(value: string, source: string): string {\n  if (value.includes(CSV_DELIMITER)) throw new Error(`${source} accepts one profile name`);\n  if (!value.trim()) throw new Error(`${source} requires a value`);\n  return value;\n}\n\n/**\n * Reports any variable left over from the retired `AGENT_HARNESS_*` namespace.\n *\n * Called before anything reads the environment, so a stale export fails the\n * session instead of being quietly outvoted by a default. Both prefixes are\n * built from the constants above rather than written inline, because a\n * search-and-replace over this repository is exactly what retired the old one.\n */\nexport function assertNoRetiredEnvironment(environment: NodeJS.ProcessEnv): void {\n  const stale = Object.keys(environment)\n    .filter((key) => key.startsWith(RETIRED_ENV_PREFIX))\n    .sort();\n  if (stale.length === 0) return;\n  const replacements = stale.map((key) => `${key} -> ${CURRENT_ENV_PREFIX}${key.slice(RETIRED_ENV_PREFIX.length)}`);\n  throw new Error(\n    `The ${RETIRED_ENV_PREFIX}* environment was replaced by ${CURRENT_ENV_PREFIX}*. Unset or rename: ${replacements.join(', ')}`,\n  );\n}\n\n/**\n * The major mode a nested run inherits from its launcher, or the default.\n *\n * The removed spelling throws rather than being ignored. A stale\n * `export DOOMPI_LAYER=dev` left in a shell profile would otherwise silently\n * select `copilot`, which is the exact confusion the rename removed.\n */\nexport function resolveInheritedMajorMode(\n  environment: NodeJS.ProcessEnv,\n  defaultMajorMode: string = DEFAULT_MAJOR_MODE,\n): string {\n  assertNoRetiredEnvironment(environment);\n  if (environment[REMOVED_DOOMPI_LAYER_ENV]) {\n    throw new Error(`${REMOVED_DOOMPI_LAYER_ENV} was replaced by ${DOOMPI_MAJOR_MODE_ENV}`);\n  }\n  const inherited = environment[DOOMPI_MAJOR_MODE_ENV];\n  return parseMajorMode(inherited || defaultMajorMode, inherited ? DOOMPI_MAJOR_MODE_ENV : 'defaultMajorMode');\n}\n\n/** The profile a run inherits from its environment, validated once up front. */\nexport function resolveInheritedProfile(environment: NodeJS.ProcessEnv): string | undefined {\n  const inherited = environment[DOOMPI_PROFILE_ENV] || undefined;\n  if (inherited?.includes(CSV_DELIMITER)) throw new Error(`${DOOMPI_PROFILE_ENV} accepts one profile name`);\n  return inherited;\n}\n\n/** Directories inherited from the launcher environment. */\nexport function resolveAdditionalDirectories(environment: NodeJS.ProcessEnv, baseDirectory: string): string[] {\n  return (environment[DOOMPI_ADDITIONAL_DIRECTORIES_ENV] ?? '')\n    .split(path.delimiter)\n    .filter(Boolean)\n    .map((directory) => path.resolve(baseDirectory, directory));\n}\n"],"mappings":"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"}