@karmaniverous/get-dotenv 5.0.0-2 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,12 @@
1
- # get-dotenv
2
-
3
- ## Requirements
4
-
5
- - Node.js >= 20 (this repository pins 22.19.0 for CI/reproducibility)
1
+ > **_Load, expand, and compose environment variables from a deterministic dotenv cascade, then execute commands under that context. Use `get-dotenv` as a library, a CLI, or a plugin-first host to build dotenv-aware tooling with cross‑platform shell control, CI‑friendly capture, and clear diagnostics._**
6
2
 
7
- ## API Reference
8
- Generated API documentation is hosted at:
9
- - https://docs.karmanivero.us/get-dotenv
3
+ # get-dotenv
10
4
 
11
- The site is built with TypeDoc from the source code in this repository.
5
+ [![npm version](https://img.shields.io/npm/v/@karmaniverous/get-dotenv.svg)](https://www.npmjs.com/package/@karmaniverous/get-dotenv)
6
+ ![Node Current](https://img.shields.io/node/v/@karmaniverous/get-dotenv) <!-- TYPEDOC_EXCLUDE -->
7
+ [![docs](https://img.shields.io/badge/docs-website-blue)](https://docs.karmanivero.us/get-dotenv)
8
+ [![changelog](https://img.shields.io/badge/changelog-latest-blue.svg)](https://github.com/karmaniverous/get-dotenv/tree/main/CHANGELOG.md)<!-- /TYPEDOC_EXCLUDE -->
9
+ [![license](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](https://github.com/karmaniverous/get-dotenv/tree/main/LICENSE.md)
12
10
 
13
11
  Load environment variables with a cascade of environment-aware dotenv files. You can:
14
12
 
@@ -44,6 +42,18 @@ You can always use `getdotenv` directly on the command line, but its REAL power
44
42
 
45
43
  When you plug your own [`commander`](https://www.npmjs.com/package/commander) CLI commands into the `getdotenv` base, they will execute within all of the environmental context created above!
46
44
 
45
+ ## Requirements
46
+
47
+ - Node.js >= 20 (this repository pins 22.19.0 for CI/reproducibility)
48
+
49
+ ## API Reference
50
+
51
+ Generated API documentation is hosted at:
52
+
53
+ - https://docs.karmanivero.us/get-dotenv
54
+
55
+ The site is built with TypeDoc from the source code in this repository.
56
+
47
57
  ## Testing
48
58
 
49
59
  This project uses Vitest with the V8 coverage provider. Run:
@@ -60,8 +70,7 @@ npm install @karmaniverous/get-dotenv
60
70
 
61
71
  ## Scaffold
62
72
 
63
- You can scaffold config files and a host-based CLI skeleton using the built-in
64
- init command. Templates are shipped with the package and copied verbatim.
73
+ You can scaffold config files and a host-based CLI skeleton using the built-in init command. Templates are shipped with the package and copied verbatim.
65
74
 
66
75
  Examples:
67
76
 
@@ -83,20 +92,13 @@ npx getdotenv init ./apps/toolbox \
83
92
 
84
93
  Collision flow (when a destination file exists):
85
94
 
86
- - Interactive prompt: [o]verwrite, [e]xample, [s]kip, or their “all” variants
87
- [O]/[E]/[S].
88
- - Non-interactive detection:
89
- - Treated as `--yes` (Skip All) unless `--force` is provided (Overwrite All).
90
- - Considered non-interactive when stdin or stdout is not a TTY OR when a
91
- CI-like environment variable is present (`CI`, `GITHUB_ACTIONS`,
92
- `BUILDKITE`, `TEAMCITY_VERSION`, `TF_BUILD`).
93
- - Precedence:
94
- - `--force` > `--yes` > auto-detect (non-interactive => Skip All).
95
+ - Interactive prompt: [o]verwrite, [e]xample, [s]kip, or their “all” variants [O]/[E]/[S].
96
+ - Non-interactive detection: Treated as `--yes` (Skip All) unless `--force` is provided (Overwrite All). Considered non-interactive when stdin or stdout is not a TTY OR when a CI-like environment variable is present (`CI`, `GITHUB_ACTIONS`, `BUILDKITE`, `TEAMCITY_VERSION`, `TF_BUILD`).
97
+ - Precedence: `--force` > `--yes` > auto-detect (non-interactive => Skip All).
95
98
  - Options overview:
96
99
  - `--config-format <json|yaml|js|ts>`
97
100
  - `--with-local` to generate `.local` alongside public config (JSON/YAML)
98
- - `--cli-name <string>` for token substitution (`__CLI_NAME__`) in the CLI
99
- skeleton
101
+ - `--cli-name <string>` for token substitution (`__CLI_NAME__`) in the CLI skeleton
100
102
  - `--force` to overwrite all; `--yes` to skip all
101
103
 
102
104
  Notes:
@@ -106,14 +108,15 @@ Notes:
106
108
 
107
109
  ## Usage
108
110
 
109
- ````js
111
+ ```js
110
112
  import { getDotenv } from '@karmaniverous/get-dotenv';
111
113
 
112
- const dotenv = await getDotenv(options);```
114
+ const dotenv = await getDotenv(options);
115
+ ```
113
116
 
114
117
  Options can be passed programmatically or set in a `getdotenv.config.json` file in your project root directory. The same file also sets default options for the `getdotenv` CLI or any child CLI you spawn from it.
115
118
 
116
- See the [child CLI example repo](https://github.com/karmaniverous/get-dotenv-child#configuration) for an extensiive discussion of the various config options and how & where to set them.
119
+ See the [child CLI example repo](https://github.com/karmaniverous/get-dotenv-child#configuration) for an extensive discussion of the various config options and how & where to set them.
117
120
 
118
121
  ## Dynamic Processing
119
122
 
@@ -126,10 +129,10 @@ export default {
126
129
  SOME_DYNAMIC_VARIABLE: (dotenv) => someLogic(dotenv),
127
130
  ANOTHER_DYNAMIC_VARIABLE: (dotenv) =>
128
131
  someOtherLogic(dotenv.SOME_DYNAMIC_VARIABLE),
129
- ONE_MORE_TIME: ({ DESTRUCTRED_VARIABLE, ANOTHER_DYNAMIC_VARIABLE }) =>
130
- DESTRUCTRED_VARIABLE + ANOTHER_DYNAMIC_VARIABLE,
132
+ ONE_MORE_TIME: ({ DESTRUCTURED_VARIABLE, ANOTHER_DYNAMIC_VARIABLE }) =>
133
+ DESTRUCTURED_VARIABLE + ANOTHER_DYNAMIC_VARIABLE,
131
134
  };
132
- ````
135
+ ```
133
136
 
134
137
  If the value corresponding to a key is a function, it will be executed with the current state of `dotenv` as its single argument and the result applied back to the `dotenv` object. Otherwise, the value will just be applied back to `dotenv`. (Although if you're going to do that then you might as well just create a public global variable in the first place.)
135
138
 
@@ -146,9 +149,7 @@ export default {
146
149
  };
147
150
  ```
148
151
 
149
- If `esbuild` is not installed and a direct import fails, get-dotenv attempts a
150
- simple fallback for single-file `.ts` modules without imports; otherwise it will
151
- throw with clear guidance to install `esbuild`.
152
+ If `esbuild` is not installed and a direct import fails, get-dotenv attempts a simple fallback for single-file `.ts` modules without imports; otherwise it will throw with clear guidance to install `esbuild`.
152
153
 
153
154
  Programmatic users can skip files entirely and pass dynamic variables directly:
154
155
 
@@ -175,8 +176,7 @@ Notes:
175
176
  - Install `esbuild` (`npm i -D esbuild`).
176
177
 
177
178
  - “Unable to load dynamic TypeScript file …”:
178
- - Install `esbuild`. A simple transpile fallback exists only for trivial
179
- single-file modules; any imports in `dynamic.ts` require `esbuild` bundling.
179
+ - Install `esbuild`. A simple transpile fallback exists only for trivial single-file modules; any imports in `dynamic.ts` require `esbuild` bundling.
180
180
 
181
181
  ## Command Line Interface
182
182
 
@@ -218,7 +218,8 @@ You can also use `getdotenv` from the command line:
218
218
  # --dynamic-path <string> dynamic variables path (.js or .ts; .ts is auto-compiled when esbuild is available, otherwise precompile)
219
219
  # --paths <string> dotenv-expanded delimited list of paths to dotenv directory (default: "./")
220
220
  # --paths-delimiter <string> paths delimiter string (default: " ")
221
- # --paths-delimiter-pattern <string> paths delimiter regex pattern# --private-token <string> dotenv-expanded token indicating private variables (default: "local")
221
+ # --paths-delimiter-pattern <string> paths delimiter regex pattern
222
+ # --private-token <string> dotenv-expanded token indicating private variables (default: "local")
222
223
  # --vars-delimiter <string> vars delimiter string (default: " ")
223
224
  # --vars-delimiter-pattern <string> vars delimiter regex pattern
224
225
  # --vars-assignor <string> vars assignment operator string (default: "=")
@@ -277,7 +278,7 @@ Note that `batch` executes its commands in sequence, rather than in parallel!
277
278
 
278
279
  To understand why, imagine running `npm install` in a dozen repos from the same command line. The visual feedback would be impossible to follow, and if something broke you'd have a really hard time figuring out why.
279
280
 
280
- Instead, everything runs in sequence, and you get a clear record of exactly what heppened and where. Also worth noting that many complex processes are resource hogs: you would not _want_ to run a dozen Serverless deployments at once!
281
+ Instead, everything runs in sequence, and you get a clear record of exactly what happened and where. Also worth noting that many complex processes are resource hogs: you would not _want_ to run a dozen Serverless deployments at once!
281
282
 
282
283
  Meanwhile, [this issue](https://github.com/karmaniverous/get-dotenv/issues/7) documents the parallel-processing option requirement. Feel free to submit a PR!
283
284
 
@@ -285,9 +286,7 @@ Meanwhile, [this issue](https://github.com/karmaniverous/get-dotenv/issues/7) do
285
286
 
286
287
  ### Authoring npm scripts and the `-c`/`--cmd` alias
287
288
 
288
- When you run commands via `npm run`, flags after `--` are forwarded to your script
289
- and may be applied to the inner shell command instead of `getdotenv` unless you
290
- structure your script carefully.
289
+ When you run commands via `npm run`, flags after `--` are forwarded to your script and may be applied to the inner shell command instead of `getdotenv` unless you structure your script carefully.
291
290
 
292
291
  - Anti-pattern:
293
292
 
@@ -301,22 +300,17 @@ structure your script carefully.
301
300
  ```json
302
301
  { "scripts": { "script": "getdotenv -c 'echo $APP_SETTING'" } }
303
302
  ```
304
- Now `npm run script -- -e dev` applies `-e` to `getdotenv`, which loads and expands
305
- variables before executing the inner command.
303
+ Now `npm run script -- -e dev` applies `-e` to `getdotenv`, which loads and expands variables before executing the inner command.
306
304
 
307
305
  Notes:
308
306
 
309
307
  - `-c`/`--cmd` is an alias of the `cmd` subcommand; do not use both in a single invocation.
310
- - On POSIX shells, prefer single quotes to prevent the outer shell from expanding `$VAR`
311
- before Node sees it. On PowerShell, single quotes are also literal.
312
- - Script-level shell overrides (`scripts[name].shell`) still take precedence over the global
313
- `--shell`.
308
+ - On POSIX shells, prefer single quotes to prevent the outer shell from expanding `$VAR` before Node sees it. On PowerShell, single quotes are also literal.
309
+ - Script-level shell overrides (`scripts[name].shell`) still take precedence over the global `--shell`.
314
310
 
315
311
  Important:
316
312
 
317
- - When using the parent alias `--cmd` with a Node eval payload, quote the entire
318
- payload as a single token so Commander does not treat `-e/--eval` as
319
- getdotenv’s `-e, --env` flag.
313
+ - When using the parent alias `--cmd` with a Node eval payload, quote the entire payload as a single token so Commander does not treat `-e/--eval` as getdotenv’s `-e, --env` flag.
320
314
  - POSIX example:
321
315
  ```
322
316
  getdotenv --cmd 'node -e "console.log(process.env.APP_SETTING ?? \"\")"'
@@ -325,23 +319,19 @@ Important:
325
319
  ```
326
320
  getdotenv --cmd 'node -e "console.log(process.env.APP_SETTING ?? \"\")"'
327
321
  ```
328
- - If you do not need to pass additional parent flags after the command, you can
329
- prefer the subcommand form instead:
322
+ - If you do not need to pass additional parent flags after the command, you can prefer the subcommand form instead:
330
323
  ```
331
324
  getdotenv --shell-off cmd node -e "console.log(process.env.APP_SETTING ?? '')"
332
325
  ```
333
326
 
334
327
  Diagnostics and CI capture:
335
328
 
336
- - To capture child stdout/stderr deterministically (e.g., in CI), either set
337
- the environment variable `GETDOTENV_STDIO=pipe` or pass `--capture`. Outputs
338
- are buffered and re-emitted after completion.
329
+ - To capture child stdout/stderr deterministically (e.g., in CI), either set the environment variable `GETDOTENV_STDIO=pipe` or pass `--capture`. Outputs are buffered and re-emitted after completion.
339
330
  - For debugging environment composition, use:
340
331
  ```
341
332
  getdotenv --trace [keys...] cmd node -e "0"
342
333
  ```
343
- When provided without keys, `--trace` emits a concise origin line for every
344
- key (parent | dotenv | unset) to stderr before the child process launches.
334
+ When provided without keys, `--trace` emits a concise origin line for every key (parent | dotenv | unset) to stderr before the child process launches.
345
335
 
346
336
  ---
347
337
 
@@ -349,21 +339,17 @@ Diagnostics and CI capture:
349
339
 
350
340
  - [Cascade and precedence](./guides/cascade.md)
351
341
  - [Shell execution behavior and quoting](./guides/shell.md)
342
+ - [Config files and overlays](./guides/config.md)
343
+ - [Plugin-first host and plugins](./guides/plugins.md)
352
344
 
353
345
  The guides are also included in the [hosted API docs](https://docs.karmanivero.us/get-dotenv).
354
346
 
355
- ---
356
-
357
347
  ## Generated CLI
358
348
 
359
- This package still supports generating a standalone CLI for your projects.
360
- For most use cases we recommend the new plugin-first host because it resolves
361
- dotenv context once per invocation, supports composable plugins, and provides
362
- better subprocess control and diagnostics. If you prefer a thin, fixed
363
- command surface with defaults baked into config, the generated CLI can be a
364
- good fit.
349
+ This package still supports generating a standalone CLI for your projects. For most use cases we recommend the new plugin-first host because it resolves dotenv context once per invocation, supports composable plugins, and provides better subprocess control and diagnostics. If you prefer a thin, fixed command surface with defaults baked into config, the generated CLI can be a good fit.
350
+
351
+ See the [Generated CLI guide](https://docs.karmanivero.us/get-dotenv/guides/generated-cli) for details
365
352
 
366
- See the Generated CLI guide for details:
367
- https://docs.karmanivero.us/get-dotenv/guides/generated-cli
353
+ ---
368
354
 
369
355
  See more great templates & tools on [my GitHub Profile](https://github.com/karmaniverous)!
package/dist/cliHost.cjs CHANGED
@@ -3,9 +3,9 @@
3
3
  var commander = require('commander');
4
4
  var fs = require('fs-extra');
5
5
  var packageDirectory = require('package-directory');
6
+ var url = require('url');
6
7
  var path = require('path');
7
8
  var zod = require('zod');
8
- var url = require('url');
9
9
  var YAML = require('yaml');
10
10
  var nanoid = require('nanoid');
11
11
  var dotenv = require('dotenv');
@@ -984,6 +984,8 @@ const computeContext = async (customOptions, plugins, hostMetaUrl) => {
984
984
 
985
985
  const HOST_META_URL = (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cliHost.cjs', document.baseURI).href));
986
986
  const CTX_SYMBOL = Symbol('GetDotenvCli.ctx');
987
+ const OPTS_SYMBOL = Symbol('GetDotenvCli.options');
988
+ const HELP_HEADER_SYMBOL = Symbol('GetDotenvCli.helpHeader');
987
989
  /**
988
990
  * Plugin-first CLI host for get-dotenv. Extends Commander.Command.
989
991
  *
@@ -1000,15 +1002,33 @@ class GetDotenvCli extends commander.Command {
1000
1002
  _plugins = [];
1001
1003
  /** One-time installation guard */
1002
1004
  _installed = false;
1005
+ /** Optional header line to prepend in help output */
1006
+ [HELP_HEADER_SYMBOL];
1003
1007
  constructor(alias = 'getdotenv') {
1004
1008
  super(alias);
1005
1009
  // Ensure subcommands that use passThroughOptions can be attached safely.
1006
1010
  // Commander requires parent commands to enable positional options when a
1007
1011
  // child uses passThroughOptions.
1008
1012
  this.enablePositionalOptions();
1013
+ // Configure grouped help: show only base options in default "Options";
1014
+ // append App/Plugin sections after default help.
1015
+ this.configureHelp({
1016
+ visibleOptions: (cmd) => {
1017
+ const all = cmd.options ??
1018
+ [];
1019
+ return all.filter((opt) => {
1020
+ const group = opt.__group;
1021
+ return group === 'base';
1022
+ });
1023
+ },
1024
+ });
1025
+ this.addHelpText('beforeAll', () => {
1026
+ const header = this[HELP_HEADER_SYMBOL];
1027
+ return header && header.length > 0 ? `${header}\n\n` : '';
1028
+ });
1029
+ this.addHelpText('afterAll', (ctx) => this.#renderOptionGroups(ctx.command));
1009
1030
  // Skeleton preSubcommand hook: produce a context if absent, without
1010
- // mutating process.env. The passOptions hook (when installed) will
1011
- // compute the final context using merged CLI options; keeping
1031
+ // mutating process.env. The passOptions hook (when installed) will // compute the final context using merged CLI options; keeping
1012
1032
  // loadProcess=false here avoids leaking dotenv values into the parent
1013
1033
  // process env before subcommands execute.
1014
1034
  this.hook('preSubcommand', async () => {
@@ -1040,11 +1060,96 @@ class GetDotenvCli extends commander.Command {
1040
1060
  getCtx() {
1041
1061
  return this[CTX_SYMBOL];
1042
1062
  }
1063
+ /**
1064
+ * Retrieve the merged root CLI options bag (if set by passOptions()).
1065
+ * Downstream-safe: no generics required.
1066
+ */
1067
+ getOptions() {
1068
+ return this[OPTS_SYMBOL];
1069
+ }
1070
+ /** Internal: set the merged root options bag for this run. */
1071
+ _setOptionsBag(bag) {
1072
+ this[OPTS_SYMBOL] = bag;
1073
+ }
1043
1074
  /** * Convenience helper to create a namespaced subcommand.
1044
1075
  */
1045
1076
  ns(name) {
1046
1077
  return this.command(name);
1047
1078
  }
1079
+ /**
1080
+ * Tag options added during the provided callback as 'app' for grouped help.
1081
+ * Allows downstream apps to demarcate their root-level options.
1082
+ */
1083
+ tagAppOptions(fn) {
1084
+ const root = this;
1085
+ const originalAddOption = root.addOption.bind(root);
1086
+ const originalOption = root.option.bind(root);
1087
+ const tagLatest = (cmd, group) => {
1088
+ const optsArr = cmd.options;
1089
+ if (Array.isArray(optsArr) && optsArr.length > 0) {
1090
+ const last = optsArr[optsArr.length - 1];
1091
+ last.__group = group;
1092
+ }
1093
+ };
1094
+ root.addOption = function patchedAdd(opt) {
1095
+ opt.__group = 'app';
1096
+ return originalAddOption(opt);
1097
+ };
1098
+ root.option = function patchedOption(...args) {
1099
+ const ret = originalOption(...args);
1100
+ tagLatest(this, 'app');
1101
+ return ret;
1102
+ };
1103
+ try {
1104
+ return fn(root);
1105
+ }
1106
+ finally {
1107
+ root.addOption = originalAddOption;
1108
+ root.option = originalOption;
1109
+ }
1110
+ }
1111
+ /**
1112
+ * Branding helper: set CLI name/description/version and optional help header.
1113
+ * If version is omitted and importMetaUrl is provided, attempts to read the
1114
+ * nearest package.json version (best-effort; non-fatal on failure).
1115
+ */
1116
+ async brand(args) {
1117
+ const { name, description, version, importMetaUrl, helpHeader } = args;
1118
+ if (typeof name === 'string' && name.length > 0)
1119
+ this.name(name);
1120
+ if (typeof description === 'string')
1121
+ this.description(description);
1122
+ let v = version;
1123
+ if (!v && importMetaUrl) {
1124
+ try {
1125
+ const fromUrl = url.fileURLToPath(importMetaUrl);
1126
+ const pkgDir = await packageDirectory.packageDirectory({ cwd: fromUrl });
1127
+ if (pkgDir) {
1128
+ const txt = await fs.readFile(`${pkgDir}/package.json`, 'utf-8');
1129
+ const pkg = JSON.parse(txt);
1130
+ if (pkg.version)
1131
+ v = pkg.version;
1132
+ }
1133
+ }
1134
+ catch {
1135
+ // best-effort only
1136
+ }
1137
+ }
1138
+ if (v)
1139
+ this.version(v);
1140
+ // Help header:
1141
+ // - If caller provides helpHeader, use it.
1142
+ // - Otherwise, when a version is known, default to "<name> v<version>".
1143
+ if (typeof helpHeader === 'string') {
1144
+ this[HELP_HEADER_SYMBOL] = helpHeader;
1145
+ }
1146
+ else if (v) {
1147
+ // Use the current command name (possibly overridden by 'name' above).
1148
+ const header = `${this.name()} v${v}`;
1149
+ this[HELP_HEADER_SYMBOL] = header;
1150
+ }
1151
+ return this;
1152
+ }
1048
1153
  /**
1049
1154
  * Register a plugin for installation (parent level).
1050
1155
  * Installation occurs on first resolveAndLoad() (or explicit install()).
@@ -1083,7 +1188,71 @@ class GetDotenvCli extends commander.Command {
1083
1188
  for (const p of this._plugins)
1084
1189
  await run(p);
1085
1190
  }
1191
+ // Render App/Plugin grouped options appended after default help.
1192
+ #renderOptionGroups(cmd) {
1193
+ const all = cmd.options ?? [];
1194
+ const byGroup = new Map();
1195
+ for (const o of all) {
1196
+ const opt = o;
1197
+ const g = opt.__group;
1198
+ if (!g || g === 'base')
1199
+ continue; // base handled by default help
1200
+ const rows = byGroup.get(g) ?? [];
1201
+ rows.push({
1202
+ flags: opt.flags ?? '',
1203
+ description: opt.description ?? '',
1204
+ });
1205
+ byGroup.set(g, rows);
1206
+ }
1207
+ if (byGroup.size === 0)
1208
+ return '';
1209
+ const renderRows = (title, rows) => {
1210
+ const width = Math.min(40, rows.reduce((m, r) => Math.max(m, r.flags.length), 0));
1211
+ const lines = rows
1212
+ .map((r) => {
1213
+ const pad = ' '.repeat(Math.max(2, width - r.flags.length + 2));
1214
+ return ` ${r.flags}${pad}${r.description}`.trimEnd();
1215
+ })
1216
+ .join('\n');
1217
+ return `\n${title}:\n${lines}\n`;
1218
+ };
1219
+ let out = '';
1220
+ // App options (if any)
1221
+ const app = byGroup.get('app');
1222
+ if (app && app.length > 0) {
1223
+ out += renderRows('App options', app);
1224
+ }
1225
+ // Plugin groups sorted by id
1226
+ const pluginKeys = Array.from(byGroup.keys()).filter((k) => k.startsWith('plugin:'));
1227
+ pluginKeys.sort((a, b) => a.localeCompare(b));
1228
+ for (const k of pluginKeys) {
1229
+ const id = k.slice('plugin:'.length) || '(unknown)';
1230
+ const rows = byGroup.get(k) ?? [];
1231
+ if (rows.length > 0) {
1232
+ out += renderRows(`Plugin options — ${id}`, rows);
1233
+ }
1234
+ }
1235
+ return out;
1236
+ }
1086
1237
  }
1087
1238
 
1239
+ /**
1240
+ * Helper to retrieve the merged root options bag from any action handler
1241
+ * that only has access to thisCommand. Avoids structural casts.
1242
+ */
1243
+ const readMergedOptions = (cmd) => {
1244
+ // Ascend to the root command
1245
+ let root = cmd;
1246
+ while (root.parent) {
1247
+ root = root.parent;
1248
+ }
1249
+ const hostAny = root;
1250
+ return typeof hostAny.getOptions === 'function'
1251
+ ? hostAny.getOptions()
1252
+ : root
1253
+ .getDotenvCliOptions;
1254
+ };
1255
+
1088
1256
  exports.GetDotenvCli = GetDotenvCli;
1089
1257
  exports.definePlugin = definePlugin;
1258
+ exports.readMergedOptions = readMergedOptions;
@@ -1,5 +1,13 @@
1
- import { ZodType } from 'zod';
2
1
  import { Command } from 'commander';
2
+ import { ZodType } from 'zod';
3
+
4
+ /**
5
+ * Scripts table shape (configurable shell type).
6
+ */
7
+ type ScriptsTable<TShell extends string | boolean = string | boolean> = Record<string, string | {
8
+ cmd: string;
9
+ shell?: TShell;
10
+ }>;
3
11
 
4
12
  /**
5
13
  * A minimal representation of an environment key/value mapping.
@@ -95,6 +103,76 @@ interface GetDotenvOptions {
95
103
  useConfigLoader?: boolean;
96
104
  }
97
105
 
106
+ type Scripts = Record<string, string | {
107
+ cmd: string;
108
+ shell?: string | boolean;
109
+ }>;
110
+ /**
111
+ * Options passed programmatically to `getDotenvCli`.
112
+ */
113
+ interface GetDotenvCliOptions extends Omit<GetDotenvOptions, 'paths' | 'vars'> {
114
+ /**
115
+ * Logs CLI internals when true.
116
+ */
117
+ debug?: boolean;
118
+ /**
119
+ * When true, capture child stdout/stderr and re-emit after completion.
120
+ * Useful for tests/CI. Default behavior is streaming via stdio: 'inherit'.
121
+ */
122
+ capture?: boolean;
123
+ /**
124
+ * A delimited string of paths to dotenv files.
125
+ */
126
+ paths?: string;
127
+ /**
128
+ * A delimiter string with which to split `paths`. Only used if
129
+ * `pathsDelimiterPattern` is not provided.
130
+ */
131
+ pathsDelimiter?: string;
132
+ /**
133
+ * A regular expression pattern with which to split `paths`. Supersedes
134
+ * `pathsDelimiter`.
135
+ */
136
+ pathsDelimiterPattern?: string;
137
+ /**
138
+ * Scripts that can be executed from the CLI, either individually or via the batch subcommand.
139
+ */
140
+ scripts?: Scripts;
141
+ /**
142
+ * Determines how commands and scripts are executed. If `false` or
143
+ * `undefined`, commands are executed as plain Javascript using the default
144
+ * execa parser. If `true`, commands are executed using the default OS shell
145
+ * parser. Otherwise the user may provide a specific shell string (e.g.
146
+ * `/bin/bash`)
147
+ */
148
+ shell?: string | boolean;
149
+ /**
150
+ * A delimited string of key-value pairs declaratively specifying variables &
151
+ * values to be loaded in addition to any dotenv files.
152
+ */
153
+ vars?: string;
154
+ /**
155
+ * A string with which to split keys from values in `vars`. Only used if
156
+ * `varsDelimiterPattern` is not provided.
157
+ */
158
+ varsAssignor?: string;
159
+ /**
160
+ * A regular expression pattern with which to split variable names from values
161
+ * in `vars`. Supersedes `varsAssignor`.
162
+ */
163
+ varsAssignorPattern?: string;
164
+ /**
165
+ * A string with which to split `vars` into key-value pairs. Only used if
166
+ * `varsDelimiterPattern` is not provided.
167
+ */
168
+ varsDelimiter?: string;
169
+ /**
170
+ * A regular expression pattern with which to split `vars` into key-value
171
+ * pairs. Supersedes `varsDelimiter`.
172
+ */
173
+ varsDelimiterPattern?: string;
174
+ }
175
+
98
176
  /** * Per-invocation context shared with plugins and actions. */
99
177
  type GetDotenvCliCtx<TOptions extends GetDotenvOptions = GetDotenvOptions> = {
100
178
  optionsResolved: TOptions;
@@ -102,6 +180,7 @@ type GetDotenvCliCtx<TOptions extends GetDotenvOptions = GetDotenvOptions> = {
102
180
  plugins?: Record<string, unknown>;
103
181
  pluginConfigs?: Record<string, unknown>;
104
182
  };
183
+ declare const HELP_HEADER_SYMBOL: unique symbol;
105
184
  /**
106
185
  * Plugin-first CLI host for get-dotenv. Extends Commander.Command.
107
186
  *
@@ -114,10 +193,13 @@ type GetDotenvCliCtx<TOptions extends GetDotenvOptions = GetDotenvOptions> = {
114
193
  * NOTE: This host is additive and does not alter the legacy CLI.
115
194
  */
116
195
  declare class GetDotenvCli<TOptions extends GetDotenvOptions = GetDotenvOptions> extends Command {
196
+ #private;
117
197
  /** Registered top-level plugins (composition happens via .use()) */
118
198
  private _plugins;
119
199
  /** One-time installation guard */
120
200
  private _installed;
201
+ /** Optional header line to prepend in help output */
202
+ private [HELP_HEADER_SYMBOL];
121
203
  constructor(alias?: string);
122
204
  /**
123
205
  * Resolve options (strict) and compute dotenv context. * Stores the context on the instance under a symbol.
@@ -127,9 +209,33 @@ declare class GetDotenvCli<TOptions extends GetDotenvOptions = GetDotenvOptions>
127
209
  * Retrieve the current invocation context (if any).
128
210
  */
129
211
  getCtx(): GetDotenvCliCtx<TOptions> | undefined;
212
+ /**
213
+ * Retrieve the merged root CLI options bag (if set by passOptions()).
214
+ * Downstream-safe: no generics required.
215
+ */
216
+ getOptions(): GetDotenvCliOptions | undefined;
217
+ /** Internal: set the merged root options bag for this run. */
218
+ _setOptionsBag(bag: GetDotenvCliOptions): void;
130
219
  /** * Convenience helper to create a namespaced subcommand.
131
220
  */
132
221
  ns(name: string): Command;
222
+ /**
223
+ * Tag options added during the provided callback as 'app' for grouped help.
224
+ * Allows downstream apps to demarcate their root-level options.
225
+ */
226
+ tagAppOptions<T>(fn: (root: Command) => T): T;
227
+ /**
228
+ * Branding helper: set CLI name/description/version and optional help header.
229
+ * If version is omitted and importMetaUrl is provided, attempts to read the
230
+ * nearest package.json version (best-effort; non-fatal on failure).
231
+ */
232
+ brand(args: {
233
+ name?: string;
234
+ description?: string;
235
+ version?: string;
236
+ importMetaUrl?: string;
237
+ helpHeader?: string;
238
+ }): Promise<this>;
133
239
  /**
134
240
  * Register a plugin for installation (parent level).
135
241
  * Installation occurs on first resolveAndLoad() (or explicit install()).
@@ -187,5 +293,11 @@ type DefineSpec = Omit<GetDotenvCliPlugin, 'children' | 'use'> & {
187
293
  */
188
294
  declare const definePlugin: (spec: DefineSpec) => GetDotenvCliPlugin;
189
295
 
190
- export { GetDotenvCli, definePlugin };
191
- export type { DefineSpec, GetDotenvCliPlugin };
296
+ /**
297
+ * Helper to retrieve the merged root options bag from any action handler
298
+ * that only has access to thisCommand. Avoids structural casts.
299
+ */
300
+ declare const readMergedOptions: (cmd: Command) => GetDotenvCliOptions | undefined;
301
+
302
+ export { GetDotenvCli, definePlugin, readMergedOptions };
303
+ export type { DefineSpec, GetDotenvCliCtx, GetDotenvCliOptions, GetDotenvCliPlugin, ScriptsTable };