feat(config): store aube settings outside npmrc#517
Conversation
Greptile SummaryThis PR introduces
Confidence Score: 5/5Safe to merge; the config split, symlink fix, and precedence wiring are all consistent with runtime behaviour and covered by new unit and integration tests. The effective read/write precedence (user .npmrc beats aube config, project .npmrc beats both) is implemented correctly and confirmed by the new bats test. All error paths that could leave stale state now propagate with ? or emit tracing::warn!. The only finding is a doc-string walk-order description that is backwards relative to the actual insertion order, which does not affect runtime correctness. The user-facing doc strings in mod.rs, aube.usage.kdl, and docs/cli/ describe the merged walk order as ~/.npmrc then user aube config, but the actual insertion order is reversed; worth correcting so users are not misled about which store wins on conflict. Important Files Changed
Reviews (3): Last reviewed commit: "[autofix.ci] apply automated fixes" | Re-trigger Greptile |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e1613c3. Configure here.
…605) ## Summary - `aube config set/delete` wrote into a sibling temp file and renamed it over the path, which replaced a symlinked `~/.config/aube/config.toml` with a regular file - Resolve the symlink target before calling `atomic_write`, mirroring the fix [#517](https://github.com/endevco/aube/pull/517) shipped for `~/.npmrc` - Expose `symlink_target_or_self` as `pub(crate)` in `npmrc.rs` so `aube_config.rs` can reuse it without duplicating Closes [#603](https://github.com/endevco/aube/discussions/603). ## Test plan - [x] `cargo test -p aube commands::config::aube_config` — new `save_preserves_symlink` test passes - [x] `cargo test -p aube commands::npmrc::tests::save_preserves_symlink` — existing npmrc symlink test still passes - [x] `cargo clippy -p aube --all-targets -- -D warnings` - [x] `cargo fmt --check` 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk: narrowly changes the write path resolution for `aube config` saves and adds a Unix-only regression test; behavior for non-symlink paths should remain unchanged. > > **Overview** > `aube config` writes now preserve a symlinked `~/.config/aube/config.toml` by resolving `path` to its symlink target before calling `atomic_write`, avoiding replacing the symlink with a regular file during rename-based atomic saves. > > The existing `npmrc` helper `symlink_target_or_self` is made `pub(crate)` for reuse, and a Unix-only test is added to assert symlink preservation and correct content updates. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 99215e3. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A user upgrading from a pre-#517 aube has aube-owned keys still living in `~/.npmrc` (where old aube used to write them). `aube config delete <key>` would then fail with a bare "not set in config.toml", with no hint about the active value in `~/.npmrc`. aube still doesn't modify `.npmrc` for aube-known keys — it's shared with npm/pnpm/yarn, which is the whole point of #601. But the error now surfaces the stale entry's location and tells the user to edit it manually (or override it from `config.toml` via `aube config set`). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… config.toml, support dotted map writes (#634) ## Summary Three rolled-up changes to `aube config set` / `delete`, motivated by [Discussion #617](https://github.com/endevco/aube/discussions/617#discussioncomment-16885128): 1. **Inverted `.npmrc` ↔ `config.toml` routing.** Writes land in `.npmrc` only for the npm-shared surface — per-host auth/cert templates, scoped registries, and a curated allowlist of npm-standard scalars. Everything else (known aube settings, pnpm-only knobs like `dangerouslyAllowAllBuilds`, unknown free-form keys) goes to aube's own config so `aube config set` stops polluting `.npmrc` with keys other npm-family tools warn about. 2. **Project-scope dotted writes for aube map settings.** `aube config set --local allowBuilds.@scope/pkg true` and `aube config set --local overrides.lodash 4.17.21` now edit one entry of the map in place, routing through `pnpm-workspace.yaml` (or `package.json#<pnpm|aube>.<map>` when no workspace yaml exists) via the same path `aube approve-builds` takes. User-scope dotted writes for these maps still error — uniformly, no per-setting special case — with a `--local` pointer, because aube only reads these maps per project today. 3. **Symmetric deletes.** `aube config delete` sweeps both aube's config and `.npmrc`, so the call works regardless of which file the value originally landed in (including legacy writes from older aube versions). ## What lands where **`.npmrc`** (multi-tool contract — npm, pnpm, yarn all read these): - Per-host auth/cert templates: `//host/:_authToken`, `//host/:_password`, `//host/:_auth`, `//host/:certfile`, `//host/:keyfile`, … - Scoped registries: `@scope:registry` - Curated allowlist of npm-standard scalars: `registry`, `email`, `proxy`/`http-proxy`/`https-proxy`, `cafile`/`ca`/`cert`/`key`, `strict-ssl`, `maxsockets`, `fetch-retries`/`fetch-timeout`, `init-author-*`/`init-license`/`init-module`/`init-version`, `userconfig`/`globalconfig`/`prefix`, `tag`/`scope`/`access`, `engine-strict`, `package-lock`, `ignore-scripts`, `node-options`, `audit`/`audit-level`, `loglevel`/`color`/`progress`, `fund`, `update-notifier`, `_auth`/`_authToken`/`_password`/`username`, `always-auth`, `before` **`~/.config/aube/config.toml` (user) / `<cwd>/.config/aube/config.toml` (project)** — aube-only: - All known aube/pnpm-only scalar settings (`autoInstallPeers`, `minimumReleaseAge`, `nodeLinker`, `packageImportMethod`, …) - `typedAccessorUnused = true` knobs that previously slipped through to `.npmrc`: `dangerouslyAllowAllBuilds`, `pnpmfilePath`, `globalPnpmfile`, `ignoredOptionalDependencies` - Genuinely unknown free-form keys **`pnpm-workspace.yaml` / `package.json#<pnpm|aube>.<map>`** (via `--local`): - Dotted writes for aube map settings: `allowBuilds.<pkg>`, `overrides.<pkg>`, `packageExtensions.<pkg>`, `allowedDeprecatedVersions.<pkg>`, `supportedArchitectures.<key>` **Rejected** (`ERR_AUBE_CONFIG_NESTED_AUBE_KEY`): - Bare object writes (`allowBuilds <json>`) — these need structural edits, not a single scalar - User-scope dotted writes for aube maps — points at `--local` - Dotted writes against a *scalar* setting (`autoInstallPeers.foo`) — no nested namespace ## Examples ``` $ aube config set registry https://r.example.com/ set registry=https://r.example.com/ (~/.npmrc) $ aube config set dangerouslyAllowAllBuilds true set dangerouslyAllowAllBuilds=true (~/.config/aube/config.toml) $ aube config set some-experimental-flag value set some-experimental-flag=value (~/.config/aube/config.toml) $ aube config set --local allowBuilds.@mongodb-js/zstd true set allowBuilds.@mongodb-js/zstd=true (./pnpm-workspace.yaml) $ aube config set --local overrides.lodash 4.17.21 set overrides.lodash=4.17.21 (./pnpm-workspace.yaml) $ aube config set allowBuilds.@mongodb-js/zstd true # user scope ERR_AUBE_CONFIG_NESTED_AUBE_KEY × `allowBuilds.@mongodb-js/zstd` only applies at project scope: │ `allowBuilds` is read from `pnpm-workspace.yaml` / │ `package.json#<pnpm|aube>.allowBuilds`, not user-scope aube config. help: use `aube config set --local allowBuilds.@mongodb-js/zstd true` to edit `pnpm-workspace.yaml#allowBuilds.@mongodb-js/zstd` … $ aube config set autoInstallPeers.foo true ERR_AUBE_CONFIG_NESTED_AUBE_KEY × `autoInstallPeers.foo` is not a writable config key: │ `autoInstallPeers` is a scalar aube setting and has no nested namespace. help: `autoInstallPeers` is type `bool` — set it directly with `aube config set autoInstallPeers <value>`. ``` ## Reads + deletes - **Reads are unchanged.** Aube still reads `.npmrc` for compatibility with existing projects, so legacy writes from older aube versions continue to take effect. - **`aube config delete` now sweeps both files** — the call works regardless of which file the value originally landed in, so users upgrading from a pre-#517 aube don't have to know where their settings live. ## New error code `ERR_AUBE_CONFIG_NESTED_AUBE_KEY` (Engine / CLI category, generic exit `1`). ## New library helper `aube_manifest::workspace::upsert_map_entry(project_dir, map_name, entry_key, yaml_value, json_value)` generalizes the write-routing the existing `add_to_allow_builds` path uses. Takes both yaml + json values so the caller controls scalar shape (bool / int / string) without the helper having to guess. ## Test plan - [x] `cargo test` — all suites green (478 + 4 + 7 + 77 + 246 + 102 + 129 + 178 + 31 + 49 + 86 + 88 + 45 + 5 + 2) - [x] `cargo clippy --all-targets -- -D warnings` clean - [x] `cargo fmt --check` clean - [x] `mise run test:bats test/config.bats` — 57/57 passing, including: - "config set routes unknown keys to user config.toml, not .npmrc" - "config get reads free-form unknown keys back from config.toml" - "config delete removes a free-form unknown key from config.toml" - "config set routes pnpm-only knobs (dangerouslyAllowAllBuilds) to config.toml" - "config set rejects bare aube map settings" - "config set keeps npm-shared keys in .npmrc" - "config set --local allowBuilds.<pkg> writes to project workspace yaml" - "config set --local allowBuilds.<pkg> appends to existing workspace yaml" - "config set --local overrides.<pkg> writes to project workspace yaml" - "config set allowBuilds.<pkg> at user scope errors with --local hint" - "config set overrides.<pkg> at user scope errors with --local hint" - "config set autoInstallPeers.foo errors: scalar settings have no nested namespace" - [x] Manual smoke confirmed for every path (project-scope writes to existing yaml + creating package.json#aube, user-scope rejection messages, scalar nested rejection) - [x] `mise run render` regenerated CLI docs (`docs/cli/commands.json`, `docs/cli/config/{set,delete}.md`) + `docs/error-codes.data.json` *This PR was generated by Claude.* <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes config write/delete routing between `.npmrc`, `config.toml`, and workspace files, which can alter where settings persist and how precedence/shadowing behaves. Adds new dotted map edit/delete behavior touching `pnpm-workspace.yaml`/`package.json`, so misclassification of keys or edit logic could lead to surprising config results. > > **Overview** > Reworks `aube config set`/`delete` to **only write npm-shared keys to `.npmrc`** (including new per-setting `npmShared` metadata plus auth/scoped-registry patterns) and to route **aube-only/pnpm-only and unknown free-form keys into `~/.config/aube/config.toml`**. > > Adds **project-scope dotted map writes/deletes** (e.g. `allowBuilds.<pkg>`, `overrides.<pkg>`) that upsert/remove a single entry in `pnpm-workspace.yaml` or fall back to `package.json#aube.<map>`, and introduces `ERR_AUBE_CONFIG_NESTED_AUBE_KEY` for invalid nested writes (user-scope map edits, bare object writes, or scalar keys with a dot). > > Updates docs/usage text and expands Bats coverage to assert the new routing, stale-config sweeping for npm-shared keys, and round-trip behavior for unknown keys and dotted map entries. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a3415fe. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->

Summary
~/.config/aube/config.tomlfor known aube-owned settingsaube config set/get/list/deleteread and write that TOML config while keeping unknown registry/auth keys in.npmrc.npmrcfiles for remaining npmrc writesContext
Addresses Discussions #513 and #516: aube-specific settings no longer need to be written into npm's user config, and
.npmrcwrites follow the symlink target instead of replacing the symlink.Validation
cargo testcargo clippy --all-targets -- -D warningscargo fmt --check && cargo checkaube config set minimum-release-age 2880writesconfig.toml,aube config get minimum-release-agereturns2880, and staleminimumReleaseAgeis removed from user.npmrcwhile registry config remainsThis PR was generated by Codex.
Note
Medium Risk
Changes configuration read/write paths and precedence by introducing
~/.config/aube/config.toml, which can affect effective settings across many commands if ordering or key classification is wrong. Also adjusts.npmrcwrites to follow symlink targets, touching auth-token storage paths.Overview
Adds a new user-scoped TOML config (
~/.config/aube/config.toml, XDG-aware) for known aube-owned settings, keeping registry/auth and unknown keys in.npmrc.Updates
aube config set/get/list/deleteand the broader settings resolver to treataubeConfigas an additional source (between.npmrcand workspace YAML), threads it through commands that build aResolveCtx, and cleans up stale user.npmrcaliases when migrating a known setting to TOML.Hardens
.npmrcpersistence by writing atomically to the symlink target (preserving symlinked dotfile setups), and refreshes generated CLI/docs/tests to reflect the new locations and precedence.Reviewed by Cursor Bugbot for commit 57b8374. Bugbot is set up for automated code reviews on this repo. Configure here.