opx init
Scan the committed tree and interactively wire up opx.
Synopsis
opx init [--auto] [--quiet] [--cleanup]
Options
| Flag | Default | Description |
|---|---|---|
--auto |
false |
Only proceed in an interactive terminal. Intended for postinstall scripts. |
--quiet |
false |
Suppress informational output when --auto exits early |
--cleanup |
false |
Opt in to the cleanup suggestions from your plugins, and the offer to delete empty files |
What it does
- Creates
.opx/config.jsonif there isn't one. - For each suggested plugin not yet enabled, asks Yes, No, or Later. "Later" records a deferral in
.opx/state.jsonfor the current branch. - Saves your choices and offers to install any missing packages with the project's package manager (npm, Yarn, pnpm, or Bun).
- Scaffolds a root
eslint.config.jscomposed from your enabled lint plugins. It never overwrites an existing file. - Offers to add
"lint": "opx lint"to yourpackage.jsonscripts. - Installs the
post-commithusky hook and updates.gitignoreto ignore.opx/*except.opx/config.json, which stays tracked. - With
--cleanup, shows the cleanup suggestions your enabled plugins make, and offers to delete empty files they replace.
Package manager
opx works out which package manager the project uses, in this order:
- The
packageManagerfield inpackage.json, such aspnpm@9.0.0. - A lockfile:
yarn.lock,pnpm-lock.yaml,bun.lockorbun.lockb,package-lock.jsonornpm-shrinkwrap.json. - The manager that launched opx (
pnpm dlx,bunx, and so on). It ranks last becausenpxreportsnpmin any project. npm.
opx looks in the project folder and then up to the repository root, so a workspace member finds the root's lockfile. It never looks above the repository root.
At a workspace root it also adds the flag some versions require: -w for pnpm (pnpm 9 and earlier refuse without it; pnpm 10 accepts it) and -W for Yarn Classic.
If the signals disagree (the packageManager field names one manager and a lockfile another, or several managers have lockfiles), opx doesn't guess. It saves your choices, then prints the install command for you to run instead of running it, and says what conflicts.
Cleanup
Cleanup is off unless asked for, because it can delete files. Run opx init --cleanup to see what each enabled plugin makes unnecessary. opx init prints those suggestions and does very little itself. It deletes a file only when all of these hold:
- The plugin suggested it, and it is a regular file inside the repository (not a symlink or a folder).
- It is empty: nothing but whitespace, comments, or
{}. - git can restore it: it is tracked and unchanged since
HEAD.
It asks once before deleting. Everything else is advice for you to act on. opx never edits package.json, never edits your eslint.config.js, and never runs a command a plugin suggests, including your package manager.
For example, with lint-js, lint-json, lint-md, or lint-ts enabled:
| What | What opx does |
|---|---|
An empty .eslintrc* or .eslintignore that git can restore |
Offers to delete it. |
A legacy ESLint config with content (.eslintrc*, .eslintignore, eslintConfig or eslintIgnore in package.json) |
Tells you opx does not apply it, and where to move what you still want: your override file if you set lint.<name>, otherwise a new flat config you point it at. For an .eslintrc* file it prints npx @eslint/migrate-config <file>, unless a flat config already exists, since that command overwrites one. You run it and delete the file. |
A package the plugin bundles (eslint, globals, …) that you list in devDependencies |
Prints the remove command for your package manager, with the version the plugin ships, if nothing in the project appears to use it. |
A package is held back, and shown under "Keeping", when:
- the project uses it: an
import,require, or quoted reference in a source or config file, or a command in apackage.jsonscript,.huskyhook, lint-staged config, or workflow; - it is also listed under
dependencies,peerDependencies, oroptionalDependencies, which removing it would change; or - opx cannot list the project's files to check.
A plugin is only consulted when it is enabled and installed. Because the suggestions ship with the plugin, they match the version you have. See Writing a detector to add them to your own plugin.
Non-interactive use
With no TTY, CI set, or OPX_NONINTERACTIVE set, init prints the suggestions and the commands to run instead of prompting. Use opx enable to apply them. With --cleanup it also prints the cleanup suggestions, and changes nothing. init --auto (postinstall) never runs cleanup, even with --cleanup.