# Coming from cosmiconfig

> A cosmiconfig alternative with zero dependencies: import { cosmiconfig } from seniority, graded 186 / 243 by cosmiconfig's own suite (YAML is the gap) — with provenance, so every resolved value can say where it came from, as text, --json data or an agent event.

Source: https://seniority.interlace.tools/docs/coming-from/cosmiconfig

**seniority** is a **cosmiconfig alternative** that also covers what `dotenv`, `rc` and
`find-up` do beside it: one resolution for flags, environment, config files, a
`package.json` field and defaults, in a fixed order, with no dependencies.

## Migrate from cosmiconfig in one import

```diff
- import { cosmiconfig } from 'cosmiconfig';
+ import { cosmiconfig } from 'seniority';
```

The root export carries cosmiconfig's surface: `cosmiconfig`, `cosmiconfigSync`, `Explorer`,
`ExplorerSync`, `defaultLoaders`, `defaultLoadersSync`, `getDefaultSearchPlaces`,
`globalConfigSearchPlaces` and `metaSearchPlaces` — all three search strategies, both caches,
`$import`, and the meta-config merge.

**YAML is the one gap.** seniority bundles no format parser, so its `loadYaml` reads the
subset of YAML that is also JSON and refuses the rest by name. Pass
`loaders: { '.yaml': yaml.load }` and you have cosmiconfig's behaviour, with the parser as
your dependency rather than everyone's.

## Is seniority compatible with cosmiconfig?

Not fully, and the number says so. cosmiconfig's own suite, vendored at 10.0.1 and unmodified
apart from the import specifier, runs against `seniority` beside a control that runs it
against real cosmiconfig:

| | passing | rate |
| :-- | --: | --: |
| `seniority` | 186 / 243 | 76.5% |
| cosmiconfig itself (control) | 240 / 243 | 98.8% |

From [Compatibility](https://burgee.interlace.tools/docs/compatibility), which `npm run compat:page` generates from the
oracle's last run; that page is the authority, and it names every case that leaves the gate.
Of the 57 cases not passing, seniority's README and that page account for each: 54 are the
YAML refusal above (the largest block is `import.test.ts`, whose fixtures are all `.yml`),
one is the harness reaching for a file path the vendored copy does not have, and two are the
XDG global-directory cases, which register only on Linux and are counted against seniority
on every platform.
Below 100%, seniority is never described as cosmiconfig-*compatible*; this is a grade.

The same page grades `seniority/lilconfig` (lilconfig's own suite), `seniority/dotenv` and
`seniority/rc`.

## What you gain over cosmiconfig

cosmiconfig finds a file. seniority resolves the value, and keeps the receipt:

- **Provenance on every value.** `resolve()` returns each option's winning source and every
  candidate it beat. `explain('region', result)` prints
  `region = "eu-2"   from config file ./app.config.json` with the unset flag and env
  candidates beneath it.
- **The same record for agents.** `explanationJson()` is that record as `--json` data, and
  `explanationEvent()` is it as an agent event — generated by the code that picked the value,
  so neither can drift from the text.
- **An order nobody can rearrange.** `flag > env > config file > package.json field >
  default`, exported as `ORDER`. A plugin can add a source between them and can never beat
  the flag the user typed — so the agent reading your `--schema` and the person reading your
  `--help` can reason about it from the outside.
- **`resolve` is pure.** No filesystem, no `process.env`: the environment is passed in, so it
  is testable without a process.

## When to switch from cosmiconfig

- "Why is this set to that?" is a question your users or agents ask, and nobody can answer
  it without reading the source.
- You want config, env and flags in one precedence rather than three hand-written ones.
- You do not need YAML, or you are content to pass the parser in.

The API, the discovery rules and the other drop-in paths are on
[seniority](/docs).
