await program.resolveAndLoad(...).cli.getCtx() inside any plugin mount/action.readMergedOptions(thisCommand).import { definePlugin } from '@karmaniverous/get-dotenv/cliHost';
export const helloPlugin = () =>
definePlugin({
ns: 'hello',
setup(cli) {
cli.description('Say hello').action(() => {
const ctx = cli.getCtx();
console.log('dotenv keys:', Object.keys(ctx.dotenv).length);
});
},
});
Use groupPlugins to group child plugins under a shared prefix (e.g. smoz getdotenv init).
import { groupPlugins } from '@karmaniverous/get-dotenv/cliHost';
import { initPlugin } from '@karmaniverous/get-dotenv/plugins';
program.use(groupPlugins({ ns: 'getdotenv' }).use(initPlugin()));
Prefer the plugin-bound helper createPluginDynamicOption to render help text that reflects the resolved configuration for that specific plugin instance.
import { z } from 'zod';
import { definePlugin } from '@karmaniverous/get-dotenv/cliHost';
const helloConfigSchema = z.object({
loud: z.boolean().optional().default(false),
});
export const helloPlugin = () => {
const plugin = definePlugin({
ns: 'hello',
configSchema: helloConfigSchema,
setup(cli) {
cli
.description('Say hello')
.addOption(
// Helper infers config type
plugin.createPluginDynamicOption(
cli,
'--loud',
(
_bag,
pluginCfg, // inferred as Readonly<HelloConfig>
) =>
`print greeting in ALL CAPS${pluginCfg.loud ? ' (default)' : ''}`,
),
)
.action(() => {
const pluginCfg = plugin.readConfig(cli);
// use pluginCfg.loud
});
},
});
return plugin;
};
To reflect defaults in subcommand help without creating separate plugins:
createPluginDynamicOption.const sub = cli.command('sub');
sub.addOption(
plugin.createPluginDynamicOption(
sub, // target the subcommand
'--mode <val>',
(_bag, pluginCfg) => `mode (default: ${pluginCfg.sub.mode})`,
),
);
For plugins with multiple subcommands (e.g. aws dynamodb create), follow the Resolver Pattern.
create: { version: ... }).action handler simply calls a resolver, then a service. Keep it thin.(flags, config, envRef) => ServiceInput.
pluginCfg values.${VAR} support. Use dotenvExpand(val, { ...process.env, ...ctx.dotenv }).cli.install() to verify commands/options exist (smoke test).Always build a normalized child env:
import { buildSpawnEnv } from '@karmaniverous/get-dotenv';
const env = buildSpawnEnv(process.env, ctx.dotenv);
Honor capture contract:
import {
readMergedOptions,
runCommand,
shouldCapture,
} from '@karmaniverous/get-dotenv/cliHost';
const bag = readMergedOptions(thisCommand);
const capture = shouldCapture(bag.capture);
await runCommand('echo OK', bag.shell ?? false, {
env,
stdio: capture ? 'pipe' : 'inherit',
});
Expand at action time using the resolved context:
import { dotenvExpand } from '@karmaniverous/get-dotenv';
const raw = String(opts.tableName ?? '');
const envRef = { ...process.env, ...cli.getCtx().dotenv };
const expanded = dotenvExpand(raw, envRef) ?? raw;
Use these in your plugin setup to validate numeric flags at parse time.
import {
parseFiniteNumber,
parsePositiveInt,
parseNonNegativeInt,
} from '@karmaniverous/get-dotenv';
cli.option('--limit <n>', 'max items', parsePositiveInt('limit'));
ensureForce)Use this helper to protect destructive actions.
import { ensureForce } from '@karmaniverous/get-dotenv/cliHost';
cli.action((args, opts) => {
if (!ensureForce(opts.force, 'delete table')) return;
// proceed...
});
traceChildEnv({ parentEnv, dotenv, keys?, redact?, ... })redactDisplay(value, ...)maybeWarnEntropy(key, value, origin, opts, write)These are presentation-only helpers to mirror the shipped CLI behavior.