Add --explain to a CLI
Give a CLI a --explain <option> flag that prints where a value came from and what it beat, and --explain --json for scripts and agents — from the record resolve() already keeps.
Every CLI with a config file eventually gets the question "why is this set to that?". With seniority the answer is already in the result, so the flag takes a few lines:
import { explain, explanation, explanationJson, resolve } from 'seniority';
const specs = {
region: { type: 'string', default: 'us-1' },
retries: { type: 'number', default: 2 },
};
const argv = process.argv.slice(2);
const flags = {};
const target = argv.includes('--explain') ? argv[argv.indexOf('--explain') + 1] : undefined;
if (argv.includes('--region')) flags.region = argv[argv.indexOf('--region') + 1];
const result = resolve(specs, {
flags,
env: { MYTOOL_RETRIES: '5' }, // process.env in a real program
envPrefix: 'MYTOOL',
config: { path: 'mytool.config.json', data: { region: 'eu-2' }, lines: { region: 2 } },
});
if (target === undefined) console.log(result.values);
else if (argv.includes('--json')) console.log(JSON.stringify(explanationJson(explanation(target, result))));
else process.stdout.write(explain(target, result));region = "eu-2" from config file mytool.config.json:2
candidates: flag --region (unset), env MYTOOL_REGION (unset), default "us-1"region = "ap-1" from flag --region
candidates: env MYTOOL_REGION (unset), config file mytool.config.json:2 "eu-2", default "us-1"{"option":"retries","value":"5","source":"env","location":"MYTOOL_RETRIES","candidates":[{"source":"flag","location":"--retries","set":false},{"source":"env","location":"MYTOOL_RETRIES","set":true,"value":"5"},{"source":"config","location":"mytool.config.json","set":false},{"source":"default","location":"default","set":true,"value":2}]}retries is the string "5": the environment is text, and resolve converts only booleans
from it. Validation reports a number that arrived as a string, naming
the variable.
A CLI built on burgee gets config explain for free,
from the same resolver.
Coming from rc
An rc alternative with a drop-in path: import rc from seniority/rc, graded 1 / 1 by rc's own test — one exit-code bit, not a case count — then rc's file stack and merge with none of its four dependencies, and a resolver that says where every value came from.
Config in a monorepo
Let a workspace package find its own config file, or the repository root's, without ever reading a config from above the repository — with search() and stopAt.