wyvrnpm 2.4.1 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@ A simple, private C++ package manager that works with any static file hosting pr
4
4
 
5
5
  There is no central registry. You control where packages are hosted.
6
6
 
7
- > **README version: 2.4.0** — Highlights since 2.1.0: `wyvrnpm add <name>` for dependency authoring, `HeaderOnlyLib` packages that survive cross-arch `find_package()`, `WYVRN_ARCH_SUFFIX` + the `WYVRN_APPLY_ARCH_SUFFIX(<target>)` helper so x64/x86 artefacts coexist on disk, `wyvrnpm build --generator / -G` to swap CMake generators from the CLI, package `options`, version ranges (`^`, `~`, explicit comparators), `--format=json` output, and `--upload-built` to warm the registry with source-built artefacts.
7
+ > **README version: 2.8.3** — Highlights since 2.4.0: non-ABI build-time `conf` block (CMake cache variables via recipe / team profile / `wyvrn.local.json` / CLI `--conf`), per-package `source` pin for multi-source defense, profile inheritance via `extends`, pattern-based `settingsOverrides` (`boost*: {...}`), `wyvrnpm cache list` / `cache prune` for selective source-build-cache cleanup, `install --dry-run` (JSON plan without side effects), `install --build=always` to force source-rebuild, `--token-env` for CI credentials, CloudFront/WAF-compatible HTTP provider (sends `User-Agent`, surfaces non-404 errors), publish preserves dependency ranges verbatim with lock snapshot moved to informational `publishedLock`, and a raft of security hardening (Zip Slip, MSVC arch allow-list). Full list in `claude/EVALUATION.md`.
8
8
 
9
9
  ---
10
10
 
@@ -97,6 +97,37 @@ wyvrnpm configure profile set-default release
97
97
 
98
98
  Once a default profile is saved, `install` and `publish` use it automatically — no flags required.
99
99
 
100
+ ### Profile inheritance — `extends` (2.8.0+)
101
+
102
+ Teams often end up with a small fleet of near-duplicate profiles (`windows-msvc-debug`, `windows-msvc-release`, `windows-msvc-x86`, …) that drift over time. A profile can inherit from another via `extends`:
103
+
104
+ ```json
105
+ {
106
+ "extends": "windows-msvc-base",
107
+ "compiler.cppstd": "23"
108
+ }
109
+ ```
110
+
111
+ Or from multiple parents (later entries override earlier):
112
+
113
+ ```json
114
+ {
115
+ "extends": ["windows-base", "team-defaults"],
116
+ "compiler.runtime": "static"
117
+ }
118
+ ```
119
+
120
+ Rules:
121
+
122
+ - `[settings]` fields merge per-key — child wins.
123
+ - The `conf` block (if present) merges per-leaf — child overrides single keys without dropping siblings.
124
+ - Overlay value `null` explicitly unsets a parent field (Conan's `None` semantics).
125
+ - Inheritance cycles are detected and fail with the full trail (`alpha → beta → alpha`).
126
+ - Missing parent names fail fast — no silent drop.
127
+ - `extends` itself is stripped from the resolved profile and is **not** folded into `profileHash`, so `{ "extends": "base" }` with no overrides hashes byte-identically to `base`.
128
+
129
+ `wyvrnpm configure profile show <name>` prints the raw `extends:` chain alongside the resolved `[settings]` so you can inspect inheritance without grepping JSON.
130
+
100
131
  ---
101
132
 
102
133
  ## Commands
@@ -161,7 +192,7 @@ Resolves the full dependency graph and downloads all packages.
161
192
  3. **Source build** (with `--build=missing`) — clones the package's source at the published `gitSha`, resolves its own dependencies, invokes CMake through the shared toolchain generator, and caches the result under `%LOCALAPPDATA%\wyvrnpm\bc\`. On Windows/MSVC with a Ninja or Make generator, `vcvarsall.bat` is auto-activated.
162
193
  4. **v1 legacy path** — only when no v2 entry exists for the package at all.
163
194
 
164
- The pre-2.1 "same OS + arch, any compiler" loose fallback is removed from the default path. It remains reachable for one release cycle via `WYVRNPM_ALLOW_LOOSE_COMPAT=1` and prints a deprecation warning.
195
+ The pre-2.1 "same OS + arch, any compiler" loose fallback is no longer available. Publish a build for your profile, add a declarative `compatibility` block to the recipe, or rerun with `--build=missing` to source-build.
165
196
 
166
197
  ```bash
167
198
  # Use configured sources and the default build profile
@@ -180,6 +211,13 @@ wyvrnpm install \
180
211
 
181
212
  # Build any missing binary from source
182
213
  wyvrnpm install --build=missing
214
+
215
+ # Force every dep to be rebuilt from source (debugging / reproducibility check)
216
+ wyvrnpm install --build=always
217
+
218
+ # Print the resolved install plan WITHOUT downloading (CI-friendly with --format=json)
219
+ wyvrnpm install --dry-run
220
+ wyvrnpm install --dry-run --format=json
183
221
  ```
184
222
 
185
223
  **Options**
@@ -193,6 +231,8 @@ wyvrnpm install --build=missing
193
231
  | `--timeout` | `300` | HTTP request timeout in seconds. |
194
232
  | `--manifest` | `./wyvrn.json` | Path to the manifest file. |
195
233
  | `--root` | `./` | Project root (packages extracted to `{root}/wyvrn_internal/`). |
234
+ | `--dry-run` | `false` | Run the resolver + profile/options math, print the install plan, and exit. No download, no `wyvrn.lock`, no `wyvrn_internal/`, no toolchain. Combine with `--format=json` for CI ingestion (payload carries `command: "install-dry-run"`). |
235
+ | `--conf` | — | Non-ABI build-time override. Repeatable. Format: `<namespace.leaf>=<value>` — e.g. `--conf cmake.cache.CHROMA_ENABLE_TEST=ON`. Layered: CLI > `wyvrn.local.json` > named-profile `conf` > recipe. Does **not** fold into `profileHash`. See [Build-time configuration (`conf`)](#build-time-configuration-conf). |
196
236
 
197
237
  Packages are extracted to `wyvrn_internal/{name}/`. A `wyvrn.lock` file is written alongside the manifest recording the exact resolved versions, profile, and per-package SHA256. On subsequent installs the lock file pins all previously resolved versions — only newly added dependencies are resolved fresh.
198
238
 
@@ -215,6 +255,16 @@ After download, `install` also generates:
215
255
 
216
256
  The optional helper `WYVRN_APPLY_ARCH_SUFFIX(<target> [BASE_NAME <name>])` from the bundled `macros.cmake` applies the suffix to a target's `OUTPUT_NAME` so x64 and x86 builds of the same library coexist on disk without overwriting each other. Target linkage (`target_link_libraries`) is unaffected — only the produced filename changes.
217
257
 
258
+ The bundled `variables.cmake` also suffixes the consumer's own output directories with `WYVRN_ARCH_SUFFIX` so multi-arch builds from one source tree don't clobber each other:
259
+
260
+ | Profile arch | Archive/library dir | Runtime (DLL/EXE) dir |
261
+ |---|---|---|
262
+ | `x86_64` / `armv8` | `output/lib64/` | `output/bin64/<CONFIG>/` |
263
+ | `x86` / `armv7` | `output/lib/` | `output/bin/<CONFIG>/` |
264
+ | unmapped | `output/lib/` | `output/bin/<CONFIG>/` |
265
+
266
+ Prior to `2.5.0` the runtime directory was always `output/bin/<CONFIG>/` regardless of arch — a multi-arch consumer building x64 then x86 would silently overwrite the first arch's DLLs. Scripts that hard-code `output/bin/…` expecting x64 payloads need to switch to `output/bin64/…` after upgrading.
267
+
218
268
  ```cmake
219
269
  project(String CXX)
220
270
  add_library(String STATIC ...)
@@ -235,6 +285,13 @@ wyvrnpm build
235
285
  # Build Debug, verbose compiler output
236
286
  wyvrnpm build --config Debug --verbose
237
287
 
288
+ # Build multiple configs in one go — configure runs once, build loops per config
289
+ wyvrnpm build --config Debug,Release --install
290
+
291
+ # Build every config the recipe declares (recipe's build.configs, or all four
292
+ # canonical configs if no recipe is present). Pairs naturally with --install.
293
+ wyvrnpm build --all-configs --install
294
+
238
295
  # Use a specific profile's preset
239
296
  wyvrnpm build --profile release
240
297
 
@@ -268,7 +325,8 @@ wyvrnpm build -- -j 8
268
325
  |---|---|---|
269
326
  | `--preset` / `-P` | `wyvrn-<profile>` | CMake configure preset name. |
270
327
  | `--profile` / `-p` | *(config / "default")* | Build profile — determines the default preset name. |
271
- | `--config` / `-c` | `Release` | Build configuration (`Debug` \| `Release` \| `RelWithDebInfo` \| `MinSizeRel`). |
328
+ | `--config` / `-c` | `Release` | Build configuration (`Debug` \| `Release` \| `RelWithDebInfo` \| `MinSizeRel`). Comma-separated to build multiple in one invocation (e.g. `Debug,Release`) — configure runs once, build (+ `--install`) loops per config. |
329
+ | `--all-configs` | `false` | Build every config from the recipe's `build.configs` (or all four canonical configs when no recipe is present). Takes precedence over `--config`. |
272
330
  | `--target` / `-t` | *(all)* | Specific CMake target to build. |
273
331
  | `--verbose` / `-v` | `false` | Pass `--verbose` to `cmake --build`. |
274
332
  | `--clean` | `false` | Remove the build directory before configuring. |
@@ -303,7 +361,9 @@ wyvrnpm publish --source default
303
361
  # Explicit destination URL
304
362
  wyvrnpm publish --source s3://my-bucket/packages --aws-profile my-sso
305
363
 
306
- # HTTP server with token auth
364
+ # HTTP server with token auth (CI-preferred: --token-env avoids argv leakage)
365
+ wyvrnpm publish --source https://pkg.corp.com --token-env WYVRN_HTTP_TOKEN
366
+ # Equivalent for interactive use; --token lands in shell history / ps output
307
367
  wyvrnpm publish --source https://pkg.corp.com --token mytoken
308
368
 
309
369
  # Local directory or SMB share
@@ -325,6 +385,7 @@ wyvrnpm publish --force
325
385
  | `--profile` / `-p` | *(config / "default")* | Named build profile to publish under. |
326
386
  | `--aws-profile` | *(config)* | AWS SSO profile for S3 authentication. |
327
387
  | `--token` | *(config)* | Bearer token for HTTP server authentication. |
388
+ | `--token-env` | *(config)* | Name of an env var holding the bearer token. Preferred for CI — the literal value never lands in `argv` or shell history. |
328
389
  | `--path` | `.` | Directory to zip and publish. |
329
390
  | `--manifest` | `./wyvrn.json` | Path to the manifest file. |
330
391
  | `--force` / `-f` | `false` | Overwrite an existing published version (same version + profile hash). |
@@ -370,6 +431,128 @@ wyvrnpm clean --build-cache
370
431
  | Option | Default | Description |
371
432
  |---|---|---|
372
433
  | `--build-cache` | `false` | Also remove the source-build cache under `%LOCALAPPDATA%\wyvrnpm\bc\` (Unix: `~/.cache/wyvrnpm/bc/`). |
434
+ | `--uploaded-built` | `false` | Wipe only the local record of what this machine has uploaded via `--upload-built` (the `.uploaded-to.json` sidecars). Does **not** touch the registry, the artefacts, or `wyvrn_internal/`. |
435
+
436
+ For selective cache pruning (keep last N variants, drop entries older than N days), use `wyvrnpm cache prune` instead — see below.
437
+
438
+ ---
439
+
440
+ ### `wyvrnpm cache` (2.8.0+)
441
+
442
+ Inspect and selectively prune the machine-wide source-build cache at `%LOCALAPPDATA%\wyvrnpm\bc\` (Unix: `~/.cache/wyvrnpm/bc/`). Useful once `--upload-built` adoption balloons cache size on dev rigs — `clean --build-cache` is the blunt alternative that wipes everything.
443
+
444
+ ```bash
445
+ # List every cached entry as a table
446
+ wyvrnpm cache list
447
+
448
+ # Filter by package name (glob)
449
+ wyvrnpm cache list "boost*"
450
+
451
+ # Machine-readable output for CI
452
+ wyvrnpm cache list --format json
453
+
454
+ # Keep the 2 most-recently-touched variants per (name, version)
455
+ wyvrnpm cache prune --keep-last 2
456
+
457
+ # Drop entries older than 30 days
458
+ wyvrnpm cache prune --older-than 30d
459
+
460
+ # Combine: keep 1 recent per version AND drop anything stale
461
+ wyvrnpm cache prune --keep-last 1 --older-than 30d
462
+
463
+ # Preview — print what would be removed, delete nothing
464
+ wyvrnpm cache prune --keep-last 2 --dry-run
465
+ ```
466
+
467
+ **`cache list` options**
468
+
469
+ | Option | Default | Description |
470
+ |---|---|---|
471
+ | `[pattern]` / `--pattern` | *(none)* | Glob over package names (`*` for any chars, `?` for one). |
472
+ | `--format` | `text` | `text` (table) or `json` (structured payload for CI). |
473
+
474
+ **`cache prune` options**
475
+
476
+ | Option | Default | Description |
477
+ |---|---|---|
478
+ | `--keep-last <N>` | *(none)* | Retain the N most-recently-touched entries **per (name, version) group**. A four-profile rig keeping-last-2 still retains the two newest profile variants of each version. |
479
+ | `--older-than <dur>` | *(none)* | Remove entries whose mtime is older than the threshold. Format: `<number><unit>`, unit ∈ `s` / `m` / `h` / `d` (e.g. `30d`, `72h`). |
480
+ | `--pattern <glob>` | *(none)* | Restrict scope to package names matching the glob. |
481
+ | `--dry-run` | `false` | Print what would be removed without deleting. |
482
+
483
+ At least one of `--keep-last` / `--older-than` is required — calling `prune` bare errors out with a pointer to `clean --build-cache` (the nuke-everything path).
484
+
485
+ ---
486
+
487
+ ### `wyvrnpm show`
488
+
489
+ Read-only registry-metadata inspector. Given `<name>[@<version>[@<profileHash>]]`, queries the first configured install source that knows the package (override with `--source`) and prints what's published.
490
+
491
+ ```bash
492
+ # Index of every published version (one HTTP round trip per source)
493
+ wyvrnpm show zlib
494
+
495
+ # Narrow to one version — fans out a per-profile query so uploadedBy,
496
+ # compatibility, and declared options show up per build
497
+ wyvrnpm show zlib@1.3.0.0
498
+
499
+ # Narrow to one specific build
500
+ wyvrnpm show zlib@1.3.0.0@a1b2c3d4e5f60718
501
+
502
+ # Pick the source explicitly (skips install-source lookup order)
503
+ wyvrnpm show zlib@1.3.0.0 --source s3://team-pkgs
504
+
505
+ # Machine-readable for CI ingestion
506
+ wyvrnpm show zlib@1.3.0.0 --format json
507
+ ```
508
+
509
+ What gets surfaced per profile:
510
+
511
+ - **origin** — `author-publish` vs `consumer-upload` (derived from whether the manifest carries an `uploadedBy` block).
512
+ - **uploadedBy** — when set, the consumer-upload provenance (`kind`, `builtFromGit`, `builtFromSha`, `uploadedAt`, `wyvrnpmVersion`).
513
+ - **compatibility** — the recipe's declarative compat block, if any.
514
+ - **declaredOptions** — the author-declared `options` map from the recipe.
515
+ - **resolvedOptions** — the resolved option values that produced this profile's `profileHash`.
516
+ - **publishedConf** — recipe + CLI `--conf` the publisher captured (informational; doesn't affect resolution).
517
+ - **signature** — when PLAN-SIGNING phase 1 ships, signer identity; absent today.
518
+
519
+ Useful for: auditing consumer uploads (who pushed this artefact, from which commit), spotting profile drift between two consumers, verifying a package's declared options before adding it to a recipe.
520
+
521
+ **Options**
522
+
523
+ | Option | Default | Description |
524
+ |---|---|---|
525
+ | `pkg` *(positional)* | *(required)* | `<name>[@<version>[@<profileHash>]]` |
526
+ | `--source` / `-s` | *(first configured install source)* | URL or configured source name. Repeatable. |
527
+ | `--format` | `text` | `text` or `json` (see [Shape — `show`](#shape--show) below). |
528
+ | `--token` / `--token-env` / `--aws-profile` | | Auth forwarded to the provider the same way `install` does. |
529
+
530
+ ---
531
+
532
+ ### `wyvrnpm install-skill`
533
+
534
+ Installs the bundled wyvrnpm Agent Skill — the authoring guide Claude Code uses when it sees a `wyvrn.json` in the workspace. Ships as a `.skill` zip inside the npm package (`claude/skills/wyvrnpm.skill`) and is extracted into `~/.claude/skills/wyvrnpm/` on demand.
535
+
536
+ ```bash
537
+ # One-shot: install the bundled skill for Claude Code
538
+ wyvrnpm install-skill --claude
539
+
540
+ # Overwrite an existing installation (e.g. after upgrading wyvrnpm)
541
+ wyvrnpm install-skill --claude --force
542
+
543
+ # Preview without writing
544
+ wyvrnpm install-skill --claude --dry-run
545
+ ```
546
+
547
+ You only need this if you want Claude Code to pick up the skill. The CLI itself works without it.
548
+
549
+ **Options**
550
+
551
+ | Option | Default | Description |
552
+ |---|---|---|
553
+ | `--claude` | `false` | Install for Claude Code (extracts to `~/.claude/skills/wyvrnpm/` + writes the `.skill` zip alongside). Required — present so a future `--cursor` / `--vscode` target can slot in without CLI churn. |
554
+ | `--force` | `false` | Overwrite an existing skill install. Without it, the command refuses to clobber. |
555
+ | `--dry-run` | `false` | Print the destination paths and intended actions without writing. |
373
556
 
374
557
  ---
375
558
 
@@ -523,7 +706,8 @@ wyvrnpm configure add-source \
523
706
  | `--name` | Yes | Unique name for this source. |
524
707
  | `--url` | Yes | Destination URL, URI, or file path. |
525
708
  | `--profile` | No | AWS SSO profile (S3 sources). |
526
- | `--token` | No | Bearer token (HTTP sources). |
709
+ | `--token` | No | Bearer token (HTTP sources). Persisted in plaintext in `config.json` — prefer `--token-env` for CI and shared setups. |
710
+ | `--token-env` | No | Name of an env var whose value is the bearer token. Only the **name** lands in `config.json`; the value is resolved fresh on every `wyvrnpm` call. |
527
711
 
528
712
  #### `wyvrnpm configure remove-source`
529
713
 
@@ -925,6 +1109,56 @@ Example:
925
1109
  }
926
1110
  ```
927
1111
 
1112
+ ### Per-dependency source pin (2.8.0+)
1113
+
1114
+ When you have more than one configured install source (e.g. a primary S3 bucket plus a mirror), you can pin a security-sensitive dep to its canonical source by name:
1115
+
1116
+ ```json
1117
+ {
1118
+ "dependencies": {
1119
+ "OpenSSL": {
1120
+ "version": "3.0.0.0",
1121
+ "source": "primary-s3"
1122
+ }
1123
+ }
1124
+ }
1125
+ ```
1126
+
1127
+ `"primary-s3"` must match a `name` from `wyvrnpm configure list`; an unknown name fails install with exit code 1 rather than silently fanning out. The pin closes two common multi-source holes:
1128
+
1129
+ - **Mirror shadowing.** A package accidentally published to the mirror can't substitute for the primary's copy.
1130
+ - **Dependency confusion.** A squatter on a secondary source can't poison the build.
1131
+
1132
+ Pins are direct-only — they don't cascade to the pinned package's own transitive deps. CLI `--source` overrides config entirely, so on a run with `--source`, any manifest pin is ignored with a warning rather than an error. The lockfile records `source: "<name>"` on pinned entries so reviewers can spot at a glance that the artefact came from the declared source; absent on non-pinned entries so pre-2.8.0 locks remain byte-identical.
1133
+
1134
+ ### Pattern-based `settingsOverrides` (2.8.0+)
1135
+
1136
+ When many deps need the same settings override — every `boost-*` family member needing `compiler.cppstd: "17"`, or every `*-debug` variant needing `compiler.runtime: "static"` — declare the pattern at the top level of the manifest instead of copy-pasting per dep:
1137
+
1138
+ ```json
1139
+ {
1140
+ "dependencies": {
1141
+ "boost-filesystem": "1.80.0.0",
1142
+ "boost-system": "1.80.0.0",
1143
+ "boost-thread": "1.80.0.0",
1144
+ "zlib": "1.3.0.0"
1145
+ },
1146
+ "settingsOverrides": {
1147
+ "boost*": { "compiler.cppstd": "17" },
1148
+ "*-debug": { "compiler.runtime": "static" }
1149
+ }
1150
+ }
1151
+ ```
1152
+
1153
+ Rules:
1154
+
1155
+ - Keys are globs — `*` matches any chars, `?` matches exactly one, literal names work too. Both ends are anchored (no substring match unless you add `*` on either side).
1156
+ - Values accept the same keys as profile overrides: `os`, `arch`, `compiler`, `compiler.version`, `compiler.cppstd`, `compiler.runtime`. Unknown keys fail install with a clear error.
1157
+ - More-specific patterns win: literal `"boost-filesystem"` beats `"boost*"`, which beats `"*"`.
1158
+ - A literal per-dep `settings` block inside `dependencies` still wins over any pattern. Use that for the one dep that deviates from the family rule.
1159
+
1160
+ Each matching pattern shifts that dep's `profileHash`, so different settings produce distinct registry artefacts — identical semantics to literal per-dep `settings`.
1161
+
928
1162
  ### Package options (`options`)
929
1163
 
930
1164
  A library author can declare **options** — compile-time feature toggles that change the produced binary's ABI. Examples: `shared` (shared vs static library), `minizip` (zlib's minizip support), `fips` (OpenSSL's FIPS mode). Each option produces a distinct `profileHash`, so every option combination gets its own artefact on the registry.
@@ -1019,6 +1253,169 @@ On Windows with MSVC and a non-Visual-Studio generator (Ninja, Make), `vcvarsall
1019
1253
 
1020
1254
  ---
1021
1255
 
1256
+ ## Build-time configuration (`conf`)
1257
+
1258
+ `conf` is wyvrnpm's channel for **non-ABI** build-time knobs — CMake cache variables, feature toggles that change `add_subdirectory()` but not the public binary, and (in future phases) other build-tool settings. It is a deliberate sibling of `options`, with the opposite hashing property:
1259
+
1260
+ | | `options` (F1) | `conf` (this section) |
1261
+ |---|---|---|
1262
+ | Folds into `profileHash`? | Yes — every combination is a distinct registry artefact. | **No.** Informational only; two builds with different `conf` still share the same `profileHash`. |
1263
+ | Typical use | `shared`/`minizip`/`fips` — things that genuinely change the binary. | `CHROMA_ENABLE_TEST`, `BUILD_DOCS`, `CMAKE_EXPORT_COMPILE_COMMANDS` — things that don't. |
1264
+ | Consumer can override? | Yes (per-dep `options` in `wyvrn.json` or `-o pkg:name=val` on CLI). | Yes (recipe → named-profile `conf` → `wyvrn.local.json` → `--conf`, in that precedence). |
1265
+
1266
+ Phase 1 (2.6.0+) supports a single namespace: `cmake.cache.<VAR>`. It flows into the generated `CMakePresets.json`'s `cacheVariables`.
1267
+
1268
+ ### The four layers
1269
+
1270
+ ```
1271
+ highest CLI --conf cmake.cache.X=ON
1272
+ │ ─────
1273
+ │ local wyvrn.local.json (per-developer, gitignored)
1274
+ │ ─────
1275
+ │ profile named-profile JSON.conf (team-shared, checked-in)
1276
+ │ ─────
1277
+ │ recipe wyvrn.json.conf (author defaults)
1278
+ ▼ ─────
1279
+ lowest (nothing — key absent)
1280
+ ```
1281
+
1282
+ **Per-leaf override, not per-branch replace.** A CLI `--conf cmake.cache.FOO=ON` on top of a recipe setting `BAR=OFF` yields both keys — it does not discard `BAR`. Any omitted layer is equivalent to an empty map at that position — a project with no `wyvrn.local.json` and no profile `conf` merges recipe + CLI exactly as phase 1.
1283
+
1284
+ ### Recipe-level defaults
1285
+
1286
+ Authors can declare defaults on their package:
1287
+
1288
+ ```json
1289
+ {
1290
+ "name": "ChromaSDK",
1291
+ "version": "2.5.0.0",
1292
+ "conf": {
1293
+ "cmake": {
1294
+ "cache": {
1295
+ "CHROMA_ENABLE_TEST": "OFF",
1296
+ "CHROMA_ENABLE_EXAMPLES": "OFF",
1297
+ "CHROMA_LOG_LEVEL": "WARNING"
1298
+ }
1299
+ }
1300
+ }
1301
+ }
1302
+ ```
1303
+
1304
+ `true`/`false` in the JSON are normalised to CMake `"ON"`/`"OFF"`; numbers are stringified; strings pass through.
1305
+
1306
+ ### Per-developer overlay — `wyvrn.local.json`
1307
+
1308
+ Drop a `wyvrn.local.json` next to `wyvrn.json` for per-machine overrides. `wyvrnpm init` scaffolds a `.gitignore` entry for new projects; existing projects get a one-line nudge on `wyvrnpm install` when the file is present but not ignored.
1309
+
1310
+ ```json
1311
+ {
1312
+ "conf": {
1313
+ "cmake": {
1314
+ "cache": {
1315
+ "CHROMA_ENABLE_TEST": "ON",
1316
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
1317
+ }
1318
+ }
1319
+ }
1320
+ }
1321
+ ```
1322
+
1323
+ Phase 1 accepts **only** the top-level key `conf` in this file — `dependencies` / `options` / anything else is a hard error, forecloseing the silent-inert footgun where a user writes `{ "dependencies": {...} }` expecting local dep overrides. Phase 2 will widen the allow-list.
1324
+
1325
+ ### Team-shared overlay — named-profile `conf` (phase 2, 2.7.0+)
1326
+
1327
+ When every developer on a team would end up with the same `wyvrn.local.json`, check that shared policy into the named profile instead. A saved profile (e.g. `%LOCALAPPDATA%\wyvrnpm\profiles\team-default.json`) may carry an optional top-level `conf` block alongside its `[settings]`:
1328
+
1329
+ ```json
1330
+ {
1331
+ "os": "Windows",
1332
+ "arch": "x86_64",
1333
+ "compiler": "msvc",
1334
+ "compiler.version": "193",
1335
+ "compiler.cppstd": "20",
1336
+ "compiler.runtime": "dynamic",
1337
+ "conf": {
1338
+ "cmake": {
1339
+ "cache": {
1340
+ "CHROMA_ENABLE_SANITIZERS": "OFF",
1341
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
1342
+ }
1343
+ }
1344
+ }
1345
+ }
1346
+ ```
1347
+
1348
+ Edit via the `configure profile set` convenience (repeatable, same `KEY=VAL` / bare-`KEY` / `KEY=` syntax as the other CLI sinks):
1349
+
1350
+ ```bash
1351
+ wyvrnpm configure profile set --name team-default --conf cmake.cache.CMAKE_EXPORT_COMPILE_COMMANDS=ON
1352
+ wyvrnpm configure profile set --name team-default --conf cmake.cache.LEGACY_FLAG= # clears it
1353
+ ```
1354
+
1355
+ Profile `conf` layers **between recipe and local**: a project's `wyvrn.local.json` (the individual override) still wins over team defaults, and CLI `--conf` still wins over everything. The distinction Conan makes between the `[conf]` block in a profile and the `-c` CLI flag maps directly onto this precedence.
1356
+
1357
+ Profile `conf` is **non-ABI**: adding or changing keys inside it does NOT shift `profileHash`, so switching a team policy between `ENABLE_SANITIZERS=ON` and `=OFF` does not invalidate already-downloaded artefacts. The invariant is locked in by `tests/profile-hash.test.js`.
1358
+
1359
+ Auto-detected profiles (when no on-disk profile file exists) never inject a `conf` layer — detection is about `[settings]` only.
1360
+
1361
+ ### CLI — ad-hoc, one invocation
1362
+
1363
+ ```bash
1364
+ # Single toggle
1365
+ wyvrnpm build --conf cmake.cache.CHROMA_ENABLE_TEST=ON
1366
+
1367
+ # Repeatable
1368
+ wyvrnpm build \
1369
+ --conf cmake.cache.CHROMA_ENABLE_TEST=ON \
1370
+ --conf cmake.cache.BUILD_DOCS=OFF
1371
+
1372
+ # Bare KEY is sugar for KEY=ON (CMake -D convention)
1373
+ wyvrnpm build --conf cmake.cache.CHROMA_ENABLE_TEST
1374
+
1375
+ # KEY= (empty RHS) clears a value inherited from a lower layer
1376
+ wyvrnpm build --conf cmake.cache.CHROMA_ENABLE_TEST=
1377
+
1378
+ # Also honoured on install and publish (publish records it as
1379
+ # `publishedConf` on the uploaded manifest — informational metadata
1380
+ # that `wyvrnpm show` surfaces).
1381
+ wyvrnpm install --conf cmake.cache.CHROMA_ENABLE_TEST=ON
1382
+ wyvrnpm publish --conf cmake.cache.BUILD_DOCS=OFF
1383
+ ```
1384
+
1385
+ `--conf` on `wyvrnpm build` passes through to `cmake --preset` as `-D` / `-U` overrides **for that invocation only** — it does not regenerate `CMakePresets.json`. Persistent per-machine overrides belong in `wyvrn.local.json`; `wyvrnpm install` regenerates the preset deterministically with the overlay applied.
1386
+
1387
+ ### Lockfile semantics
1388
+
1389
+ `wyvrn.lock` gains an informational `effectiveConf` block after each install:
1390
+
1391
+ ```json
1392
+ {
1393
+ "wyvrnVersion": 2,
1394
+ "profileHash": "a1b2c3d4e5f60718",
1395
+ "effectiveConf": {
1396
+ "cmake": { "cache": { "CHROMA_ENABLE_TEST": "ON" } }
1397
+ },
1398
+ "packages": { ... }
1399
+ }
1400
+ ```
1401
+
1402
+ This is diagnostic only — changing `conf` between installs shows up as a lockfile diff but never causes a relock or version change. Conf drift is a legitimate review signal, not an error.
1403
+
1404
+ ### Source-build boundary
1405
+
1406
+ When `--build=missing` or `--build=always` source-builds a dependency, **only that dep's own recipe `conf`** flows into its CMake invocation. The root project's merged conf (including `wyvrn.local.json`, named-profile `conf`, and CLI `--conf`) never reaches a dep's source-build. This preserves cross-consumer reproducibility — two developers source-building the same `(name, version, profileHash)` produce the same binary regardless of their local overlays or team profile, which is load-bearing for `--upload-built`.
1407
+
1408
+ ### Why this isn't `build.configure`
1409
+
1410
+ Existing authors using `build.configure: ["-DFOO=ON", "-DBAR=OFF"]` keep working unchanged. But `build.configure` has two weaknesses `conf` fixes:
1411
+
1412
+ 1. **All-or-nothing.** A consumer can't disable one flag without overriding the whole array.
1413
+ 2. **Opaque strings.** `"-DFOO=ON"` is parsed at configure time, not at manifest load.
1414
+
1415
+ Recipe authors writing new packages should prefer `conf.cmake.cache.FOO=ON` for cache variables and keep `build.configure` for non-cache args like `-G`, `-T`, or `-A`. Setting the same KEY in both `build.configure` (`-DKEY=...`) AND `conf.cmake.cache.KEY` is a recipe-normalise error — pick one route.
1416
+
1417
+ ---
1418
+
1022
1419
  ## Compatibility Rules
1023
1420
 
1024
1421
  Publishers declare an optional `compatibility` block in a package's `wyvrn.json`. It tells wyvrnpm which profile fields must match the consumer's profile exactly, and which may legitimately differ without breaking ABI. When no exact `profileHash` match exists on the registry, `install` evaluates this block against every published build for the requested `(name, version)` and picks the best compatible candidate.
@@ -1073,16 +1470,6 @@ When multiple published builds satisfy the consumer's profile, the winner is pic
1073
1470
 
1074
1471
  The chosen candidate is logged along with a per-field summary (e.g. `compiler.cppstd 20→23 (lte), compiler.runtime=dynamic`).
1075
1472
 
1076
- ### Legacy loose fallback
1077
-
1078
- The pre-2.1 "same OS + arch, any compiler" fallback is removed from the default resolution path. For one release cycle it remains reachable via an env var:
1079
-
1080
- ```bash
1081
- WYVRNPM_ALLOW_LOOSE_COMPAT=1 wyvrnpm install
1082
- ```
1083
-
1084
- This prints a deprecation warning every time it fires and will be removed in the next minor release. Prefer adding a real `compatibility` block to affected packages, or rebuilding with `--build=missing`.
1085
-
1086
1473
  ---
1087
1474
 
1088
1475
  ## Source Builds
@@ -1144,6 +1531,8 @@ Source builds are cached machine-wide, keyed by `(name, version, profileHash)`:
1144
1531
 
1145
1532
  Re-running `install --build=missing` with the same inputs short-circuits to the cached artefact. The cache root is deliberately shallow (`bc/`, not `build-cache/`) to keep nested CMake paths under Windows' 260-char limit.
1146
1533
 
1534
+ `--build=always` (2.5.0+) ignores every registry-binary path — exact-match, compat-match, and any already-extracted `wyvrn_internal/{name}/` — and forces a source-build for every dep. Useful for debugging whether a registry binary differs from what the current source tree would produce, or for reproducibility verification. The build-cache short-circuit still applies: re-running `--build=always` with unchanged inputs completes in seconds.
1535
+
1147
1536
  Clone behaviour is tri-tier:
1148
1537
 
1149
1538
  1. **Shallow-SHA fetch** (`git fetch origin <sha> --depth=1`) — fastest; works when the git server allows it.
@@ -1193,7 +1582,7 @@ wyvrnpm install --build=missing --upload-built --upload-source team-s3
1193
1582
 
1194
1583
  # Upload auth is resolved in order: CLI flag → named-source config → first configured publish source.
1195
1584
  wyvrnpm install --build=missing --upload-built --aws-profile my-sso
1196
- wyvrnpm install --build=missing --upload-built --token mytoken
1585
+ wyvrnpm install --build=missing --upload-built --token-env WYVRN_HTTP_TOKEN
1197
1586
  ```
1198
1587
 
1199
1588
  Semantics:
@@ -1353,6 +1742,15 @@ wyvrnpm build --install --install-prefix C:\out\stage
1353
1742
 
1354
1743
  # Install a specific config
1355
1744
  wyvrnpm build --install --config Debug
1745
+
1746
+ # Install multiple configs in one invocation — configure runs once,
1747
+ # build + install loops per config. Before 2.9.0 this required two
1748
+ # separate `wyvrnpm build --install ...` invocations.
1749
+ wyvrnpm build --install --config Debug,Release
1750
+
1751
+ # Install every config declared in the recipe's build.configs
1752
+ # (or all four canonical configs when no recipe is present).
1753
+ wyvrnpm build --install --all-configs
1356
1754
  ```
1357
1755
 
1358
1756
  ### What it does, in order