feat(config): scope .npmrc to npm-shared keys, route aube settings to config.toml, support dotted map writes#634
Conversation
…tings `aube config set allowBuilds.@scope/pkg true` fell through to ~/.npmrc, where aube doesn't read the dotted key and npm warns/errors about an unknown user config. Surface an explicit error (ERR_AUBE_CONFIG_NESTED_AUBE_KEY) pointing at the right edit site (`aube approve-builds` for `allowBuilds`; workspace yaml / `package.json#aube.<name>` for other maps) and leave .npmrc untouched. Discussion #617 follow-up.
Greptile SummaryThis PR inverts the
Confidence Score: 5/5Safe to merge; the write routing and symmetric delete are well-tested and logically consistent. The core routing logic (npm-shared vs config.toml vs workspace yaml) is correct, the stale-config.toml sweep prevents precedence shadowing, and the integer-to-string coercion fix is sound. The two findings are minor UX inconsistencies in the delete path: a missing error code on the user-scope dotted-delete error, and the success message showing the project directory instead of the specific file modified. crates/aube/src/commands/config/delete.rs — the user-scope error in try_delete_aube_map_entry is missing the ERR_AUBE_CONFIG_NESTED_AUBE_KEY code that the symmetric set.rs path uses, and the success message shows the CWD rather than the specific file written. Important Files Changed
Reviews (9): Last reviewed commit: "fix(config): support `delete --local <ma..." | Re-trigger Greptile |
… writes to the npm-shared surface
Inverts the `aube config set` routing so `.npmrc` writes only happen for
keys that are part of the multi-tool npm contract — per-host auth/cert
templates (`//host/:_authToken`, `//host/:_password`, …), scoped
registries (`@scope:registry`), and a curated allowlist of npm-standard
scalars (`registry`, `email`, `proxy`/`https-proxy`, `cafile`,
`strict-ssl`, `init-author-*`, …).
Everything else now lands in aube's own config
(`~/.config/aube/config.toml` at user-scope, `<cwd>/.config/aube/config.toml`
at project-scope):
- known aube/pnpm settings whose scalar value was previously routed there
- `typedAccessorUnused = true` knobs like `dangerouslyAllowAllBuilds`
- genuinely unknown free-form keys (new behavior, opt-in via direct
`.npmrc` edits if a third-party tool needs to read them)
Aube map settings (`allowBuilds`, `overrides`, `packageExtensions`, …)
are rejected up front — they need structural edits in workspace yaml or
`package.json#aube.<name>`. `aube approve-builds <pkg>` is suggested in
the error help for `allowBuilds`.
`aube config delete` now sweeps both aube's config and `.npmrc`, so the
call works regardless of which file the value originated in (including
legacy writes from older aube versions).
Reading is unchanged — aube still reads `.npmrc` for compatibility.
Discussion #617 follow-up (broader take on iki's "are there still
non-npm settings going to `.npmrc`?" question).
aube config set <prefix>.<sub> for aube map settings`aube config set --local <map>.<entry> <value>` now edits one entry of an aube map setting in place — same path `aube approve-builds` and install-time auto-deny seeding take: aube config set --local allowBuilds.@mongodb-js/zstd true aube config set --local overrides.lodash 4.17.21 Writes land in `pnpm-workspace.yaml#<map>.<entry>` when a workspace yaml exists, otherwise `package.json#<pnpm|aube>.<map>.<entry>` via the existing `edit_setting_map` / `config_write_target` helpers. Bool / integer / string values round-trip with their natural scalar type. User-scope still errors (uniformly, no per-setting special case) because aube only reads these maps per project today — the error points at `--local` rather than dropping the value into `~/.config/aube/config.toml` where nothing would read it. Addresses greptile feedback on PR #634: - drops the hard-coded `meta.name == "allowBuilds"` branch (the project-scope path succeeds and the user-scope message is uniform across all map settings) - new tests cover both the `allowBuilds` and `overrides` paths at both scopes, plus a scalar-with-dot rejection (`autoInstallPeers.foo`) aube-manifest gets a new `pub fn upsert_map_entry` that generalizes the write-routing the existing allowBuilds path uses.
…rlap settings Settings like `engineStrict`, `ignoreScripts`, `color`, `loglevel`, `httpsProxy`, `maxsockets`, and `fetchRetries` are both: - in `is_npm_shared_key` (npm reads them from `.npmrc`) - known aube settings in settings.toml Before this fix, an `aube config set engineStrict false` write would land in `.npmrc` but leave any prior `config.toml` entry intact. Since the resolver gives `userAubeConfig` higher precedence than `userNpmrc` (matches the priority install uses), the stale `config.toml` value silently shadowed the new `.npmrc` value — `aube config get` would return the old value and the user's write would appear inert. `write_npmrc` now sweeps the matching `config.toml` entries (canonical name + every literal alias) on every `.npmrc` write that touches a known aube setting. No-op for keys that aren't known aube settings (auth tokens, scoped registries, npm-only scalars). Spotted by cursor[bot] on PR #634.
`set` routes overlap keys (`engineStrict`, `strict-ssl`,
`ignore-scripts`, `https-proxy`, …) to `.npmrc` because they're in
`is_npm_shared_key`, even though they're also known aube settings.
`delete` was checking `is_aube_config_key` first and only sweeping
`config.toml` + workspace yaml — never `.npmrc` — so after
`aube config set strict-ssl false`, `aube config delete strict-ssl`
failed with the misleading "stale entry" error and left the .npmrc
line in place.
Restructured delete to mirror set's routing:
- workspace yaml swept first at project scope for known settings
- `config.toml` swept (canonical name + aliases + raw key) for
every key, covering both known settings and free-form entries
- `.npmrc` swept when the key is npm-shared OR free-form; aube-only
settings (autoInstallPeers, minimumReleaseAge, …) still get the
`.npmrc`-protection guarantee preserved by the original
missing-key error path
Spotted by cursor[bot] on PR #634. Dead `AubeConfigEdit::remove`
removed — `remove_aliases` covers every call site now.
`is_npm_shared_key` used a hardcoded match list of ~50 keys to decide whether `aube config set` should write to `.npmrc` or `config.toml`. Move that metadata next to the settings it describes: - new `npmShared: bool` field on `SettingMeta`, codegened by `aube-settings/build.rs` from a `npmShared = true` line on each applicable `settings.toml` entry - tagged the npm-shared settings: registries, httpsProxy, httpProxy, noProxy, localAddress, maxsockets, strictSsl, fetchRetries family, fetchTimeout, color, loglevel, engineStrict, ignoreScripts, nodeOptions, tag, updateNotifier - `is_npm_shared_key` is now: per-host template (`//*`) ∪ scoped registry template (`@*:registry`) ∪ `setting.npm_shared` flag Also drops the history-as-narration phrasing from docs / set.rs / delete.rs: - "still **reads** legacy `.npmrc` entries" → describe current read+write behavior, not the transition from a prior version - "aube no longer modifies" / "stale entry" → "aube doesn't modify" / "an entry exists" - removed "Migration case" comment from the aube-only-key delete test - linked the configuration.md routing rule to the settings.toml file so the source of truth is discoverable Hardcoded scalars I dropped (email, ca, cafile, _auth, init-author-*, prefix, audit, fund, …) are not in `settings.toml` so they now fall through to free-form config.toml writes. Users who specifically want those in `.npmrc` for npm to see can edit `.npmrc` directly or we add them to `settings.toml` with `npmShared = true` in a follow-up.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 6282cb0. Configure here.
`is_npm_shared_key` used `rest.contains(":registry")` which would
match `@scope:registry.foo` and `@scope:registryfoo` as if they were
scoped-registry templates and route them to `.npmrc`. The npm pattern
is exactly `@scope:registry` with nothing after — switch to
`ends_with(":registry")`.
Spotted by cursor[bot] on PR #634.
`scalar_to_yaml_json` had an integer branch that would serialize pure-digit values as YAML / JSON numbers. That breaks aube map writes like `overrides.express 4` (a valid "any 4.x" version spec) because pnpm's (and aube's) typed-`String` deserializer rejects integer values without a custom visitor. Only booleans get a typed representation now — `allowBuilds` genuinely stores `true` / `false`, but every other aube map value (override versions, package-extension ranges, deprecated version specs, …) must round-trip as a string. Spotted by greptile-apps[bot] on PR #634.
…ings
`set --local allowBuilds.<pkg> true` writes to `pnpm-workspace.yaml`
(or `package.json#aube.allowBuilds.<pkg>` if no workspace yaml), but
the dotted form had no symmetric delete path. The fall-through error
said "not set in aube config or .npmrc" while the value sat stuck in
the workspace yaml.
Mirrors the existing `try_set_aube_map_entry` flow:
- new `aube_manifest::workspace::remove_map_entry` helper that
sweeps `<map>.<entry>` from both the workspace yaml (when present)
and `<pnpm|aube>.<map>.<entry>` in `package.json`, scrubbing
empty `<map>:` containers so deletes don't leave a `{}` stub
- `try_delete_aube_map_entry` in `delete.rs` short-circuits the
dotted form before the generic delete logic runs, with the same
`--local` hint at user scope as `set`
Spotted by greptile-apps[bot] on PR #634.

Summary
Three rolled-up changes to
aube config set/delete, motivated by Discussion #617:Inverted
.npmrc↔config.tomlrouting. Writes land in.npmrconly 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 likedangerouslyAllowAllBuilds, unknown free-form keys) goes to aube's own config soaube config setstops polluting.npmrcwith keys other npm-family tools warn about.Project-scope dotted writes for aube map settings.
aube config set --local allowBuilds.@scope/pkg trueandaube config set --local overrides.lodash 4.17.21now edit one entry of the map in place, routing throughpnpm-workspace.yaml(orpackage.json#<pnpm|aube>.<map>when no workspace yaml exists) via the same pathaube approve-buildstakes. User-scope dotted writes for these maps still error — uniformly, no per-setting special case — with a--localpointer, because aube only reads these maps per project today.Symmetric deletes.
aube config deletesweeps 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)://host/:_authToken,//host/:_password,//host/:_auth,//host/:certfile,//host/:keyfile, …@scope:registryregistry,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:autoInstallPeers,minimumReleaseAge,nodeLinker,packageImportMethod, …)typedAccessorUnused = trueknobs that previously slipped through to.npmrc:dangerouslyAllowAllBuilds,pnpmfilePath,globalPnpmfile,ignoredOptionalDependenciespnpm-workspace.yaml/package.json#<pnpm|aube>.<map>(via--local):allowBuilds.<pkg>,overrides.<pkg>,packageExtensions.<pkg>,allowedDeprecatedVersions.<pkg>,supportedArchitectures.<key>Rejected (
ERR_AUBE_CONFIG_NESTED_AUBE_KEY):allowBuilds <json>) — these need structural edits, not a single scalar--localautoInstallPeers.foo) — no nested namespaceExamples
Reads + deletes
.npmrcfor compatibility with existing projects, so legacy writes from older aube versions continue to take effect.aube config deletenow sweeps both files — the call works regardless of which file the value originally landed in, so users upgrading from a pre-feat(config): store aube settings outside npmrc #517 aube don't have to know where their settings live.New error code
ERR_AUBE_CONFIG_NESTED_AUBE_KEY(Engine / CLI category, generic exit1).New library helper
aube_manifest::workspace::upsert_map_entry(project_dir, map_name, entry_key, yaml_value, json_value)generalizes the write-routing the existingadd_to_allow_buildspath uses. Takes both yaml + json values so the caller controls scalar shape (bool / int / string) without the helper having to guess.Test plan
cargo test— all suites green (478 + 4 + 7 + 77 + 246 + 102 + 129 + 178 + 31 + 49 + 86 + 88 + 45 + 5 + 2)cargo clippy --all-targets -- -D warningscleancargo fmt --checkcleanmise run test:bats test/config.bats— 57/57 passing, including:mise run renderregenerated CLI docs (docs/cli/commands.json,docs/cli/config/{set,delete}.md) +docs/error-codes.data.jsonThis PR was generated by Claude.
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 touchingpnpm-workspace.yaml/package.json, so misclassification of keys or edit logic could lead to surprising config results.Overview
Reworks
aube config set/deleteto only write npm-shared keys to.npmrc(including new per-settingnpmSharedmetadata 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 inpnpm-workspace.yamlor fall back topackage.json#aube.<map>, and introducesERR_AUBE_CONFIG_NESTED_AUBE_KEYfor 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.
Reviewed by Cursor Bugbot for commit a3415fe. Bugbot is set up for automated code reviews on this repo. Configure here.