wyvrnpm 2.20.2 → 2.21.1
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 +57 -10
- package/bin/wyvrnpm.js +5 -4
- package/claude/agents/README.md +159 -0
- package/claude/agents/wyvrnpm-onboarding.md +131 -0
- package/package.json +2 -1
- package/src/commands/install-skill.js +90 -8
- package/src/commands/link.js +56 -5
package/README.md
CHANGED
|
@@ -4,7 +4,21 @@ 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.
|
|
7
|
+
> **README version: 2.20.2**
|
|
8
|
+
>
|
|
9
|
+
> **New in 2.16.0 – 2.20.2:**
|
|
10
|
+
>
|
|
11
|
+
> - **Visual Studio architecture pinning (2.20.0).** A recipe-less Windows/MSVC consumer (one that only lists `dependencies`, no `build` recipe) used to get a generator-less `wyvrn-<profile>` preset, so `cmake --preset` fell through to CMake's implicit Visual Studio default — which targets the **host** arch (x64) when no `-A` is supplied. An `x86` / `armv8` profile therefore silently produced 64-bit binaries. `wyvrnpm install` now detects the default VS generator and bakes both `generator` and `architecture` into the emitted preset, and `wyvrnpm build` derives `-A` (`x86→Win32`, `x86_64→x64`, `armv8→ARM64`, `armv7→ARM`) for VS generators. Builds now match the profile's bitness without a recipe. (Already-published non-host-arch artefacts built via the old source-build path may still hold wrong-bitness binaries — those are rebuilt + republished out of band.)
|
|
12
|
+
> - **`wyvrnpm build` resolves the generator from the emitted preset, not the recipe (2.20.2).** The configure step now reads the generator that `cmake --preset` will *actually* use — precedence: CLI `--generator` > the `generator` baked into `CMakePresets.json` > the detected default for a generator-less preset. This drives both the `-A` pin and the vcvarsall-skip decision, so they stay consistent with reality. Fixes a class of failures where a Ninja Multi-Config preset got `-A x64` ("does not support platform specification") and skipped vcvarsall ("CMAKE_CXX_COMPILER not set") because the old code re-derived the generator from the recipe — which can legitimately diverge from the preset (an install-time `--generator`, or a recipe edited after the last install).
|
|
13
|
+
> - **CMake-path fallback (2.20.1).** `install` / `build` / source-build no longer fail outright on a machine where CMake isn't on `PATH` — `resolveCmakeExe()` falls back to the Windows default `C:\Program Files\CMake\bin\cmake.exe` before surfacing the usual "is CMake installed?" error.
|
|
14
|
+
> - **Machine-wide binary package cache (2.19.0).** Every successful v2 exact-match download is also cached under `%LOCALAPPDATA%\wyvrnpm\pkg\` (14-day TTL) so re-installs across projects skip the network. `wyvrnpm cache list` / `cache prune` gain a `--type src|pkg|all` selector covering both this and the source-build cache, and `wyvrnpm clean --pkg-cache` wipes it.
|
|
15
|
+
> - **`wyvrnpm test` (2.18.0).** A `ctest --preset wyvrn-<profile>-<config>` wrapper that auto-builds when stale, supports `-R` / `-L` filters and `--all-configs`, and emits a `--format json` summary for CI.
|
|
16
|
+
> - **Output directory restructure (2.17.0).** Compiled artefacts now land inside the CMake binary dir (`<binaryDir>/lib`, `<binaryDir>/bin`) instead of `${CMAKE_SOURCE_DIR}/output/`, so `wyvrnpm clean --all` actually catches them. Scripts that hard-coded `output/bin64/…` must switch paths — `clean --all` sweeps the legacy `output/` tree as a migration aid.
|
|
17
|
+
> - **`wyvrnpm clean` flags (2.16.0).** `--build-dir` (also wipe the active profile's CMake build tree), `--all` (scorched-earth: build dirs + `wyvrn-*` presets + legacy `output/`), and `--dry-run` (preview, write nothing).
|
|
18
|
+
>
|
|
19
|
+
> **Planned (not yet shipped):**
|
|
20
|
+
>
|
|
21
|
+
> - **Resolver → provider unification.** Today the dependency resolver (`resolve.js` / `download.js`) talks to registries through a bare `fetch` client that hard-codes the v2/v1 URL layouts, separate from the provider classes that handle publish + download. The plan is to route all metadata lookups through provider methods (`v2GetVersionsIndex` / `v2GetMeta` / `v2GetLatest` / `v1*`) so S3 SigV4, auth resolution, and provider-specific quirks live in one place. Design captured in [.claude-wyvrn-local/plans/2026-04-24-resolver-http-unify.md](.claude-wyvrn-local/plans/2026-04-24-resolver-http-unify.md). The 2.8.4 `Accept-Encoding: identity` hotfix covers the immediate CloudFront cache-split symptom in the meantime.
|
|
8
22
|
>
|
|
9
23
|
> **New in 2.15.0:**
|
|
10
24
|
>
|
|
@@ -371,7 +385,7 @@ After download, `install` also generates:
|
|
|
371
385
|
- `wyvrn_internal/wyvrn_toolchain.cmake` — CMake toolchain pointing at the resolved packages.
|
|
372
386
|
- `wyvrn_internal/wyvrn_deps.cmake` — post-`project()` include for `find_package()` wiring and runtime DLL copy.
|
|
373
387
|
- `wyvrn_internal/wyvrn_profile.json` — snapshot of the active build profile.
|
|
374
|
-
- `CMakePresets.json` (or `CMakeUserPresets.json` if the project root file is user-owned) with a `wyvrn-<profile>` configure preset. Presets for other profiles are preserved so multiple profiles coexist.
|
|
388
|
+
- `CMakePresets.json` (or `CMakeUserPresets.json` if the project root file is user-owned) with a `wyvrn-<profile>` configure preset. Presets for other profiles are preserved so multiple profiles coexist. On Windows/MSVC the preset is pinned to a concrete Visual Studio `generator` **and** `architecture` (derived from the profile arch) even when the project carries no `build` recipe — so a recipe-less `x86` / `armv8` consumer builds the correct bitness instead of CMake's implicit host-x64 default (2.20.0). Declare a `build.generator` (or a `CMakeUserPresets.json`) to opt into Ninja/Make instead.
|
|
375
389
|
|
|
376
390
|
**Toolchain variables exposed to your `CMakeLists.txt` (valid BEFORE `project()`)**:
|
|
377
391
|
|
|
@@ -471,6 +485,8 @@ wyvrnpm build -- -j 8
|
|
|
471
485
|
|
|
472
486
|
Everything after `--` is forwarded verbatim to `cmake --build` (e.g. `-j 8`).
|
|
473
487
|
|
|
488
|
+
**How the effective generator is resolved (2.20.2+).** `wyvrnpm build` decides the `-A` arch pin and the vcvarsall-skip from the generator `cmake --preset` will *actually* use — not the project's `build` recipe. Precedence: CLI `--generator` (wins, passed as `-G`) → the `generator` baked into `CMakePresets.json` at install time → the detected default Visual Studio generator for a generator-less preset. Trusting the preset matters because it can legitimately diverge from the recipe (an install-time `--generator`, or a recipe edited after the last install); pinning `-A` against a non-VS preset is a hard CMake error, so the two decisions must agree with what cmake runs.
|
|
489
|
+
|
|
474
490
|
---
|
|
475
491
|
|
|
476
492
|
### `wyvrnpm test` (2.18.0+)
|
|
@@ -620,16 +636,32 @@ Removes the `wyvrn_internal/` package cache and `wyvrn.lock`.
|
|
|
620
636
|
# Project-local cleanup
|
|
621
637
|
wyvrnpm clean
|
|
622
638
|
|
|
639
|
+
# Also wipe the active profile's CMake build tree
|
|
640
|
+
wyvrnpm clean --build-dir
|
|
641
|
+
|
|
642
|
+
# Scorched earth: build dirs + wyvrn-* presets + legacy output/ tree
|
|
643
|
+
wyvrnpm clean --all
|
|
644
|
+
|
|
623
645
|
# Also wipe the machine-wide source-build cache used by --build=missing
|
|
624
646
|
wyvrnpm clean --build-cache
|
|
647
|
+
|
|
648
|
+
# Also wipe the machine-wide binary package cache
|
|
649
|
+
wyvrnpm clean --pkg-cache
|
|
650
|
+
|
|
651
|
+
# Preview every action without deleting anything
|
|
652
|
+
wyvrnpm clean --all --dry-run
|
|
625
653
|
```
|
|
626
654
|
|
|
627
655
|
**Options**
|
|
628
656
|
|
|
629
657
|
| Option | Default | Description |
|
|
630
658
|
|---|---|---|
|
|
659
|
+
| `--build-dir` | `false` | Also remove the active profile's CMake build tree at `<binaryDirRoot>/wyvrn-<profile>/`. |
|
|
660
|
+
| `--all` | `false` | Default scrub **plus** every `<binaryDirRoot>/wyvrn-*/` build dir, the `wyvrn-*` entries stripped from `CMakePresets.json`, and the legacy `<root>/output/` tree (2.17.0 migration aid). |
|
|
631
661
|
| `--build-cache` | `false` | Also remove the source-build cache under `%LOCALAPPDATA%\wyvrnpm\bc\` (Unix: `~/.cache/wyvrnpm/bc/`). |
|
|
632
|
-
| `--
|
|
662
|
+
| `--pkg-cache` | `false` | Also remove the machine-wide binary package cache under `%LOCALAPPDATA%\wyvrnpm\pkg\` (2.19.0). Opt-in only — never wiped by the default scrub or `--all`. |
|
|
663
|
+
| `--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/`. When passed alone, the default scrub is skipped. |
|
|
664
|
+
| `--dry-run` | `false` | Log `Would remove …` for every action and write nothing. |
|
|
633
665
|
|
|
634
666
|
For selective cache pruning (keep last N variants, drop entries older than N days), use `wyvrnpm cache prune` instead — see below.
|
|
635
667
|
|
|
@@ -637,12 +669,22 @@ For selective cache pruning (keep last N variants, drop entries older than N day
|
|
|
637
669
|
|
|
638
670
|
### `wyvrnpm cache` (2.8.0+)
|
|
639
671
|
|
|
640
|
-
Inspect and selectively prune the machine-wide
|
|
672
|
+
Inspect and selectively prune the two machine-wide caches. Useful once cache size balloons on dev rigs — `clean --build-cache` / `clean --pkg-cache` are the blunt alternatives that wipe everything of one type.
|
|
673
|
+
|
|
674
|
+
| Type | Location | Populated by | TTL |
|
|
675
|
+
|---|---|---|---|
|
|
676
|
+
| `src` | `%LOCALAPPDATA%\wyvrnpm\bc\` (Unix: `~/.cache/wyvrnpm/bc/`) | source-builds (`--build=missing`) | none |
|
|
677
|
+
| `pkg` | `%LOCALAPPDATA%\wyvrnpm\pkg\` | every successful v2 exact-match download (2.19.0+) | 14 days |
|
|
678
|
+
|
|
679
|
+
`--type src|pkg|all` (default `all`) restricts both `list` and `prune` to one cache.
|
|
641
680
|
|
|
642
681
|
```bash
|
|
643
|
-
# List every cached entry as a table
|
|
682
|
+
# List every cached entry across both caches as a table
|
|
644
683
|
wyvrnpm cache list
|
|
645
684
|
|
|
685
|
+
# Only the binary package cache
|
|
686
|
+
wyvrnpm cache list --type pkg
|
|
687
|
+
|
|
646
688
|
# Filter by package name (glob)
|
|
647
689
|
wyvrnpm cache list "boost*"
|
|
648
690
|
|
|
@@ -652,8 +694,8 @@ wyvrnpm cache list --format json
|
|
|
652
694
|
# Keep the 2 most-recently-touched variants per (name, version)
|
|
653
695
|
wyvrnpm cache prune --keep-last 2
|
|
654
696
|
|
|
655
|
-
#
|
|
656
|
-
wyvrnpm cache prune --older-than 30d
|
|
697
|
+
# Prune only the source-build cache, dropping entries older than 30 days
|
|
698
|
+
wyvrnpm cache prune --type src --older-than 30d
|
|
657
699
|
|
|
658
700
|
# Combine: keep 1 recent per version AND drop anything stale
|
|
659
701
|
wyvrnpm cache prune --keep-last 1 --older-than 30d
|
|
@@ -667,12 +709,14 @@ wyvrnpm cache prune --keep-last 2 --dry-run
|
|
|
667
709
|
| Option | Default | Description |
|
|
668
710
|
|---|---|---|
|
|
669
711
|
| `[pattern]` / `--pattern` | *(none)* | Glob over package names (`*` for any chars, `?` for one). |
|
|
712
|
+
| `--type` | `all` | Which cache to inspect: `src` (source-build), `pkg` (binary), or `all`. |
|
|
670
713
|
| `--format` | `text` | `text` (table) or `json` (structured payload for CI). |
|
|
671
714
|
|
|
672
715
|
**`cache prune` options**
|
|
673
716
|
|
|
674
717
|
| Option | Default | Description |
|
|
675
718
|
|---|---|---|
|
|
719
|
+
| `--type` | `all` | Which cache to prune: `src` (source-build), `pkg` (binary), or `all`. |
|
|
676
720
|
| `--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. |
|
|
677
721
|
| `--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`). |
|
|
678
722
|
| `--pattern <glob>` | *(none)* | Restrict scope to package names matching the glob. |
|
|
@@ -729,10 +773,13 @@ Useful for: auditing consumer uploads (who pushed this artefact, from which comm
|
|
|
729
773
|
|
|
730
774
|
### `wyvrnpm install-skill`
|
|
731
775
|
|
|
732
|
-
Installs the bundled
|
|
776
|
+
Installs the bundled Claude Code assets that ship inside the npm package:
|
|
777
|
+
|
|
778
|
+
- The **wyvrnpm Agent Skill** — the authoring guide Claude uses when it sees a `wyvrn.json` in the workspace. Ships as a `.skill` zip (`claude/skills/wyvrnpm.skill`), extracted into `~/.claude/skills/wyvrnpm/`.
|
|
779
|
+
- The **onboarding agent(s)** — e.g. `wyvrnpm-onboarding`, which sets up a project to consume wyvrnpm deps or converts an existing CMake project into a wyvrnpm package. Ships as Markdown (`claude/agents/*.md`), copied into `~/.claude/agents/`.
|
|
733
780
|
|
|
734
781
|
```bash
|
|
735
|
-
# One-shot: install the bundled skill for Claude Code
|
|
782
|
+
# One-shot: install the bundled skill + agent(s) for Claude Code
|
|
736
783
|
wyvrnpm install-skill --claude
|
|
737
784
|
|
|
738
785
|
# Overwrite an existing installation (e.g. after upgrading wyvrnpm)
|
|
@@ -742,7 +789,7 @@ wyvrnpm install-skill --claude --force
|
|
|
742
789
|
wyvrnpm install-skill --claude --dry-run
|
|
743
790
|
```
|
|
744
791
|
|
|
745
|
-
You only need this if you want Claude Code to pick up the skill. The CLI itself works without it.
|
|
792
|
+
You only need this if you want Claude Code to pick up the skill + agent. The CLI itself works without it.
|
|
746
793
|
|
|
747
794
|
**Options**
|
|
748
795
|
|
package/bin/wyvrnpm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
@@ -1098,8 +1098,9 @@ yargs
|
|
|
1098
1098
|
y
|
|
1099
1099
|
.positional('name', { type: 'string', description: 'Package name to link' })
|
|
1100
1100
|
.positional('path', { type: 'string', description: 'Local path to package' })
|
|
1101
|
-
.option('list',
|
|
1102
|
-
.option('subdir',
|
|
1101
|
+
.option('list', { type: 'boolean', description: 'List all globally registered packages', default: false })
|
|
1102
|
+
.option('subdir', { type: 'string', description: 'Subdirectory within the package to link' })
|
|
1103
|
+
.option('profile', { type: 'string', description: 'Profile whose install tree to register (default: config default)' });
|
|
1103
1104
|
},
|
|
1104
1105
|
(argv) => link(argv),
|
|
1105
1106
|
)
|
|
@@ -1126,7 +1127,7 @@ yargs
|
|
|
1126
1127
|
// ── install-skill ─────────────────────────────────────────────────────────
|
|
1127
1128
|
.command(
|
|
1128
1129
|
'install-skill',
|
|
1129
|
-
'Install the bundled wyvrnpm Agent Skill into Claude Code (~/.claude/skills/
|
|
1130
|
+
'Install the bundled wyvrnpm Agent Skill + onboarding agent(s) into Claude Code (~/.claude/skills/ + ~/.claude/agents/)',
|
|
1130
1131
|
(y) => {
|
|
1131
1132
|
y
|
|
1132
1133
|
.option('claude', { type: 'boolean', description: 'Install for Claude Code', default: false })
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# wyvrnpm Agents
|
|
2
|
+
|
|
3
|
+
Claude Code **subagents** that ship with wyvrnpm. A subagent is a separate
|
|
4
|
+
Claude worker with its own context window, tool access, and a clean return
|
|
5
|
+
boundary — you delegate a self-contained task to it and get back a result,
|
|
6
|
+
without the back-and-forth cluttering your main conversation.
|
|
7
|
+
|
|
8
|
+
These are distributed the same way as the wyvrnpm **skill**: the source lives
|
|
9
|
+
here under `claude/agents/`, and an active copy is placed in your user-global
|
|
10
|
+
`~/.claude/agents/` so Claude Code picks it up in every project.
|
|
11
|
+
|
|
12
|
+
> Skill vs. agent: the **skill** (`claude/skills/wyvrnpm/`) is *knowledge* — it
|
|
13
|
+
> teaches Claude how wyvrnpm works. An **agent** is an *execution context* that
|
|
14
|
+
> does a job, loading the skill for reference. The onboarding agent below uses
|
|
15
|
+
> the skill; it does not replace it.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## `wyvrnpm-onboarding`
|
|
20
|
+
|
|
21
|
+
### What it is
|
|
22
|
+
|
|
23
|
+
A guided agent that gets a C++ developer who is **new to wyvrnpm** productive in
|
|
24
|
+
one shot. It handles two distinct goals and figures out which one you want:
|
|
25
|
+
|
|
26
|
+
| Mode | Goal | What it does |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| **A — Consume** | "I want to *use* libraries from our team registry in this project." | Configures the install source + profile, writes `wyvrn.json`, runs `wyvrnpm install`, wires your `CMakeLists.txt` (`wyvrnpm_find_dependencies()`, `wyvrn::<dep>` links, runtime-DLL copy), and confirms the build configures. |
|
|
29
|
+
| **B — Convert / publish** | "I want to turn *this* library into a wyvrnpm package others can depend on." | Reads your `CMakeLists.txt`, picks `kind` / `options` / a `build` recipe, writes `wyvrn.json` + `.wyvrnignore`, then **verifies** by running `wyvrnpm install` and `wyvrnpm build --install --all-configs` across all four configs. Hands you a ready-to-run `publish` command — it never publishes itself. |
|
|
30
|
+
|
|
31
|
+
### Why it exists
|
|
32
|
+
|
|
33
|
+
wyvrnpm has no central registry and a few first-day gotchas (4-part versions,
|
|
34
|
+
profile hashes, per-config install trees, `.wyvrnignore`). This agent removes
|
|
35
|
+
the "I don't know where to start" barrier so teammates can adopt the tool
|
|
36
|
+
without reading the full docs first.
|
|
37
|
+
|
|
38
|
+
### Prerequisites
|
|
39
|
+
|
|
40
|
+
- **wyvrnpm installed:** `npm i -g wyvrnpm` (Node.js ≥ 18). The agent checks
|
|
41
|
+
this first and stops if it's missing.
|
|
42
|
+
- **For Mode A (consume):** your team's install source URL (an S3 bucket, HTTPS
|
|
43
|
+
endpoint, or file/SMB share). The agent will configure it for you if it isn't
|
|
44
|
+
already — wyvrnpm has no default registry, so this is required to fetch
|
|
45
|
+
anything.
|
|
46
|
+
- **Claude Code**, with the agent installed (see below).
|
|
47
|
+
|
|
48
|
+
### Install / make it active
|
|
49
|
+
|
|
50
|
+
The agent file is a single Markdown file with YAML frontmatter. Put a copy where
|
|
51
|
+
Claude Code looks for agents:
|
|
52
|
+
|
|
53
|
+
The supported path is the wyvrnpm CLI — `install-skill` installs the bundled
|
|
54
|
+
agent(s) alongside the skill:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm i -g wyvrnpm
|
|
58
|
+
wyvrnpm install-skill --claude # installs skill -> ~/.claude/skills/
|
|
59
|
+
# AND agent(s) -> ~/.claude/agents/
|
|
60
|
+
wyvrnpm install-skill --claude --force # overwrite after upgrading wyvrnpm
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Or copy the file by hand:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Personal — available in every project on your machine:
|
|
67
|
+
mkdir -p ~/.claude/agents
|
|
68
|
+
cp claude/agents/wyvrnpm-onboarding.md ~/.claude/agents/
|
|
69
|
+
|
|
70
|
+
# OR project-local — shared with everyone who clones that repo:
|
|
71
|
+
mkdir -p <your-repo>/.claude/agents
|
|
72
|
+
cp claude/agents/wyvrnpm-onboarding.md <your-repo>/.claude/agents/
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Claude Code picks it up on the next session start.
|
|
76
|
+
|
|
77
|
+
### How to use it
|
|
78
|
+
|
|
79
|
+
**Implicitly** — just describe an onboarding task while you're in a C++ repo and
|
|
80
|
+
Claude routes to the agent:
|
|
81
|
+
|
|
82
|
+
- "Set up wyvrnpm in this project."
|
|
83
|
+
- "Add wyvrnpm and pull in `fmt` and `spdlog`."
|
|
84
|
+
- "Convert this library into a wyvrnpm package."
|
|
85
|
+
- "Make this repo publishable on our registry."
|
|
86
|
+
- "How do I start with wyvrnpm here?"
|
|
87
|
+
|
|
88
|
+
**Explicitly** — name it: *"Use the wyvrnpm-onboarding agent to set this up."*
|
|
89
|
+
|
|
90
|
+
If it can't tell whether you want Mode A or Mode B, it asks one short question
|
|
91
|
+
rather than guessing.
|
|
92
|
+
|
|
93
|
+
### What you get back
|
|
94
|
+
|
|
95
|
+
Every run ends with:
|
|
96
|
+
|
|
97
|
+
1. A summary — which mode it ran, the files it created/changed, and what it
|
|
98
|
+
**verified** (e.g. "install OK; all 4 configs built + installed").
|
|
99
|
+
2. The single next command to run yourself — for Mode B that's the exact
|
|
100
|
+
`wyvrnpm publish …` line plus the pre-publish checklist; for Mode A it's
|
|
101
|
+
"you're ready to build."
|
|
102
|
+
3. If something failed: the specific error and how it was fixed, or what's still
|
|
103
|
+
blocked. It won't claim success on an unvalidated manifest.
|
|
104
|
+
|
|
105
|
+
### Safety — it never writes to your team's registry
|
|
106
|
+
|
|
107
|
+
Onboarding is meant to be safe to run. The agent will **not** run any command
|
|
108
|
+
that writes to shared team state without your explicit confirmation:
|
|
109
|
+
|
|
110
|
+
- `wyvrnpm publish` / `publish --force` — it always stops here and hands the
|
|
111
|
+
command back to you.
|
|
112
|
+
- `wyvrnpm install --upload-built` — also a registry write.
|
|
113
|
+
- `wyvrnpm configure remove-source`.
|
|
114
|
+
|
|
115
|
+
Everything else it runs is local and reversible (`install`, `build`, `clean`,
|
|
116
|
+
`init`, `bootstrap`, `show`, `link`, profile/source *reads*). For
|
|
117
|
+
`configure add-source` it shows you the URL/credentials before writing them.
|
|
118
|
+
|
|
119
|
+
### Example
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
You (in an app repo): "set up wyvrnpm here and add fmt"
|
|
123
|
+
|
|
124
|
+
Agent:
|
|
125
|
+
• wyvrnpm --version → 2.21.0 ✓
|
|
126
|
+
• No install source configured — what's your team's registry URL?
|
|
127
|
+
You: https://pkg.example.com/cpp
|
|
128
|
+
Agent:
|
|
129
|
+
• configure add-source --kind install --name primary --url https://pkg.example.com/cpp
|
|
130
|
+
• profile detect → saved "default" (Windows / x86_64 / msvc 193 / C++20)
|
|
131
|
+
• wyvrnpm init → wyvrn.json (kind: ConsoleApp)
|
|
132
|
+
• wyvrnpm add fmt → "^10.x"
|
|
133
|
+
• wyvrnpm install → downloaded fmt, generated toolchain + CMakePresets.json
|
|
134
|
+
• patched CMakeLists.txt: wyvrnpm_find_dependencies();
|
|
135
|
+
target_link_libraries(MyApp PRIVATE wyvrn::fmt); wyvrnpm_finalize_targets()
|
|
136
|
+
• cmake --preset wyvrn-default → configured OK
|
|
137
|
+
|
|
138
|
+
Summary: consuming mode. fmt wired in and the build configures.
|
|
139
|
+
Next: wyvrnpm build
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Customizing
|
|
143
|
+
|
|
144
|
+
The agent is just `wyvrnpm-onboarding.md`. Edit the frontmatter to change:
|
|
145
|
+
|
|
146
|
+
- `model:` — defaults to `sonnet` (cheap/fast for procedural work); bump to
|
|
147
|
+
`opus` for trickier conversions.
|
|
148
|
+
- `tools:` — the allowlist (`Read, Grep, Glob, Edit, Write, Bash, Skill`).
|
|
149
|
+
|
|
150
|
+
The body is the system prompt; it loads the `wyvrnpm` skill for authoritative
|
|
151
|
+
command and manifest detail, so keep the two in sync.
|
|
152
|
+
|
|
153
|
+
### Out of scope
|
|
154
|
+
|
|
155
|
+
It deliberately does **not**: publish for you, set up CI pipelines, or handle
|
|
156
|
+
cross-compilation / recipe revisions (features not yet shipped). For deep
|
|
157
|
+
manifest, command, or diagnostic questions it defers to the `wyvrnpm` skill and
|
|
158
|
+
the user docs (README / the Confluence "Wyvrn Package Manager" and "WyvrnPM
|
|
159
|
+
Cmake Utils" pages).
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wyvrnpm-onboarding
|
|
3
|
+
description: >-
|
|
4
|
+
Onboard a C++ project onto wyvrnpm. Use this agent when someone wants to adopt
|
|
5
|
+
wyvrnpm and is not yet familiar with it — either (A) SET UP a new or existing
|
|
6
|
+
project to CONSUME wyvrnpm dependencies, or (B) CONVERT an existing CMake
|
|
7
|
+
project into a publishable wyvrnpm package. Triggers on "set up wyvrnpm here",
|
|
8
|
+
"add wyvrnpm to my project", "use wyvrnpm for my deps", "convert this to
|
|
9
|
+
wyvrnpm", "make this a wyvrnpm package", "how do I start with wyvrnpm", or a
|
|
10
|
+
teammate opening a C++ repo that has no wyvrn.json yet and asking to get going.
|
|
11
|
+
Drives the end-to-end flow — manifest, install, CMake wiring, and per-config
|
|
12
|
+
build verification — and STOPS before any registry-writing publish.
|
|
13
|
+
tools: Read, Grep, Glob, Edit, Write, Bash, Skill
|
|
14
|
+
model: sonnet
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
You are the wyvrnpm onboarding guide. Your job is to get a C++ developer who is
|
|
18
|
+
**new to wyvrnpm** productive: either consuming dependencies through it, or
|
|
19
|
+
turning their project into a publishable wyvrnpm package. Assume the person does
|
|
20
|
+
not know wyvrnpm yet — explain briefly as you go, and prefer running the safe
|
|
21
|
+
commands for them over telling them to run things.
|
|
22
|
+
|
|
23
|
+
wyvrnpm is a private, registry-less C++ package manager (a Node.js CLI,
|
|
24
|
+
`npm i -g wyvrnpm`). It publishes/consumes prebuilt C++ artefacts through static
|
|
25
|
+
hosting (S3 / HTTP / local dir / SMB). Every artefact is addressed by
|
|
26
|
+
`(name, version, profileHash)` where the hash encodes the toolchain profile +
|
|
27
|
+
package options, so ABI compatibility is explicit. Versions are 4-part
|
|
28
|
+
`major.minor.patch.build`.
|
|
29
|
+
|
|
30
|
+
## First, always
|
|
31
|
+
|
|
32
|
+
1. **Load the `wyvrnpm` skill** (invoke it via the Skill tool). It is the
|
|
33
|
+
authoritative reference for manifest fields, commands, and diagnostics. This
|
|
34
|
+
prompt is the orchestration; the skill is the detail. When the skill and this
|
|
35
|
+
prompt disagree, trust the skill, then the live tool output.
|
|
36
|
+
2. **Check the tool is installed:** run `wyvrnpm --version`. If it's missing,
|
|
37
|
+
tell them to `npm i -g wyvrnpm` (Node ≥ 18) and stop until it's available.
|
|
38
|
+
3. **Detect the mode** (see below). If it's genuinely ambiguous, ask one short
|
|
39
|
+
question — do not guess.
|
|
40
|
+
|
|
41
|
+
## Pick the mode
|
|
42
|
+
|
|
43
|
+
There are two distinct goals. Figure out which one applies before doing anything:
|
|
44
|
+
|
|
45
|
+
- **A — CONSUME** (most common for app/teammate onboarding): "I want to *use*
|
|
46
|
+
C++ libraries from our team registry in this project." → you'll add
|
|
47
|
+
`dependencies`, run `install`, and wire CMake so `find_package` works.
|
|
48
|
+
- **B — CONVERT/PUBLISH**: "I want to turn *this* library into a wyvrnpm package
|
|
49
|
+
others can depend on." → you'll write a `kind` + `build` recipe + options,
|
|
50
|
+
build all configs, and hand back a ready-to-run `publish` (you never publish).
|
|
51
|
+
|
|
52
|
+
Signals: a project with `add_executable` that wants third-party libs → usually A.
|
|
53
|
+
An existing OSS/library repo (`add_library`, has a public `include/`) they want
|
|
54
|
+
to share → usually B. If both could apply, ask.
|
|
55
|
+
|
|
56
|
+
## Mode A — set up a project to consume wyvrnpm deps
|
|
57
|
+
|
|
58
|
+
1. **Install source.** Run `wyvrnpm configure list`. If no install source is
|
|
59
|
+
configured, explain that wyvrnpm has no default registry and ask them for the
|
|
60
|
+
team's source URL, then `wyvrnpm configure add-source --kind install --name
|
|
61
|
+
<name> --url <url>` (show the URL/credentials before writing). Without a
|
|
62
|
+
source, `install` can't fetch anything.
|
|
63
|
+
2. **Profile.** Run `wyvrnpm configure profile show`. If none is saved, run
|
|
64
|
+
`wyvrnpm configure profile detect` (saves "default") so artefacts resolve to
|
|
65
|
+
their machine's toolchain.
|
|
66
|
+
3. **Manifest.** If there's no `wyvrn.json`, run `wyvrnpm init` (or copy a
|
|
67
|
+
per-kind template from the skill's `templates/`). Set `name`, a 4-part
|
|
68
|
+
`version`, and `kind` (usually `ConsoleApp`).
|
|
69
|
+
4. **Add deps.** For each library they want, `wyvrnpm add <name>` (resolves
|
|
70
|
+
latest, writes a caret range) or `wyvrnpm add <name>@<version>`.
|
|
71
|
+
5. **Install.** `wyvrnpm install`. This downloads to `wyvrn_internal/`, writes
|
|
72
|
+
`wyvrn.lock`, and generates the CMake toolchain + `CMakePresets.json`.
|
|
73
|
+
6. **Wire CMake.** In their top-level `CMakeLists.txt`, after `project()`:
|
|
74
|
+
call `wyvrnpm_find_dependencies()`, then `target_link_libraries(<tgt> PRIVATE
|
|
75
|
+
wyvrn::<dep> ...)`, and `wyvrnpm_finalize_targets()` at the end (copies
|
|
76
|
+
runtime DLLs on Windows). Configure via the generated preset:
|
|
77
|
+
`cmake --preset wyvrn-default` then `wyvrnpm build`. For the full toolchain
|
|
78
|
+
model and the `WYVRN_*` variables/macros, point them at the team's
|
|
79
|
+
"WyvrnPM Cmake Utils" doc.
|
|
80
|
+
7. **gitignore.** Ensure `wyvrn_internal/`, `wyvrn.lock`, `wyvrn.local.json`,
|
|
81
|
+
`build/`, `*.zip` are ignored.
|
|
82
|
+
8. **Verify** the build configures and links, then summarize.
|
|
83
|
+
|
|
84
|
+
## Mode B — convert an existing CMake project into a wyvrnpm package
|
|
85
|
+
|
|
86
|
+
Follow the skill's "convert-a-project flow" exactly. The deliverable is a
|
|
87
|
+
**validated** package, not just a `wyvrn.json`:
|
|
88
|
+
|
|
89
|
+
1. **Read the existing `CMakeLists.txt`** to choose `kind`
|
|
90
|
+
(`HeaderOnlyLib` / `StaticLib` / `DynamicLib`), declare `options` only for
|
|
91
|
+
real ABI-affecting toggles, and write a `build` recipe. For known OSS,
|
|
92
|
+
`wyvrnpm bootstrap <git-url>` drafts a first-pass manifest (clones, detects
|
|
93
|
+
kind, applies cookbook flags) — review and correct it.
|
|
94
|
+
2. **`build.configs` defaults to all four:**
|
|
95
|
+
`["Debug","Release","RelWithDebInfo","MinSizeRel"]` (note the spelling —
|
|
96
|
+
`RelWithDebInfo`). Restrict only with a concrete reason.
|
|
97
|
+
3. **Write `.wyvrnignore`** next to `wyvrn.json` — `publish` zips the whole
|
|
98
|
+
project tree, so exclude tests/examples/docs/`build/`/CI. A HeaderOnlyLib zip
|
|
99
|
+
should be well under 1 MB.
|
|
100
|
+
4. **`wyvrnpm install`** in the project root — surfaces manifest errors fast.
|
|
101
|
+
5. **`wyvrnpm build --install --all-configs`** — `--install` is mandatory; plain
|
|
102
|
+
build doesn't populate the install tree (headers + libs + `*Config.cmake`)
|
|
103
|
+
that consumers' `find_package` needs. All four configs must complete.
|
|
104
|
+
6. **Diagnose any failure** (skill's diagnostics reference) and fix the manifest
|
|
105
|
+
— don't paper over a broken config.
|
|
106
|
+
7. **STOP before `wyvrnpm publish`.** Hand back the exact `publish` command plus
|
|
107
|
+
the pre-publish checklist (changes committed, branch pushed, right profile,
|
|
108
|
+
`--source` explicit). The human runs it.
|
|
109
|
+
|
|
110
|
+
## Hard safety rails (never violate)
|
|
111
|
+
|
|
112
|
+
These write to **shared team state** and need the human's explicit yes — never
|
|
113
|
+
run them yourself as part of onboarding:
|
|
114
|
+
|
|
115
|
+
- `wyvrnpm publish` / `publish --force` — registry writes other teammates see.
|
|
116
|
+
- `wyvrnpm install --upload-built` — also a registry write.
|
|
117
|
+
- `wyvrnpm configure remove-source` — confirm first.
|
|
118
|
+
|
|
119
|
+
Safe to run without asking (local, reversible): `install` (no `--upload-built`),
|
|
120
|
+
`build`, `clean`, `show`, `link`/`unlink`, `init`, `bootstrap`, `configure list`
|
|
121
|
+
/ `profile show` / `profile detect`. For `configure add-source`, show the URL +
|
|
122
|
+
any credentials before writing.
|
|
123
|
+
|
|
124
|
+
## Finish every run with
|
|
125
|
+
|
|
126
|
+
- A short summary: which mode you ran, the files you created/changed, and what
|
|
127
|
+
you verified (e.g. "install OK; all 4 configs built + installed").
|
|
128
|
+
- The single exact next command the human should run (for Mode B, the
|
|
129
|
+
`wyvrnpm publish ...` line), or "you're ready to build" for Mode A.
|
|
130
|
+
- If anything failed, the specific error and what you changed to fix it (or what
|
|
131
|
+
remains blocked). Be honest — an unvalidated manifest is worth less than none.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wyvrnpm",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.21.1",
|
|
4
4
|
"description": "A simple, static-hosting-compatible C++ package manager",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"c++",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"src/",
|
|
22
22
|
"cmake/",
|
|
23
23
|
"claude/skills/wyvrnpm.skill",
|
|
24
|
+
"claude/agents/",
|
|
24
25
|
"README.md"
|
|
25
26
|
],
|
|
26
27
|
"scripts": {
|
|
@@ -11,16 +11,19 @@ const log = require('../logger');
|
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
// `wyvrnpm install-skill --claude`
|
|
13
13
|
//
|
|
14
|
-
// Copies the bundled wyvrnpm Agent Skill (claude/skills/wyvrnpm.skill)
|
|
15
|
-
// the user's Claude Code
|
|
14
|
+
// Copies the bundled wyvrnpm Agent Skill (claude/skills/wyvrnpm.skill) AND the
|
|
15
|
+
// bundled onboarding agents (claude/agents/*.md) into the user's Claude Code
|
|
16
|
+
// directories so Claude picks them up automatically.
|
|
16
17
|
//
|
|
17
18
|
// Works for both install shapes:
|
|
18
|
-
// - `npm i -g wyvrnpm` →
|
|
19
|
+
// - `npm i -g wyvrnpm` → assets ship under <pkg>/claude/
|
|
19
20
|
// - `git clone` of this repo → same path, just not from node_modules
|
|
20
21
|
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
22
|
+
// Destinations, per target (--claude):
|
|
23
|
+
// skill → %USERPROFILE%\.claude\skills\wyvrnpm\ (Windows)
|
|
24
|
+
// ~/.claude/skills/wyvrnpm/ (macOS / Linux)
|
|
25
|
+
// agents → %USERPROFILE%\.claude\agents\ (Windows)
|
|
26
|
+
// ~/.claude/agents/ (macOS / Linux)
|
|
24
27
|
// ---------------------------------------------------------------------------
|
|
25
28
|
|
|
26
29
|
const SKILL_NAME = 'wyvrnpm';
|
|
@@ -31,18 +34,94 @@ function bundledSkillPath() {
|
|
|
31
34
|
return path.resolve(__dirname, '..', '..', 'claude', 'skills', `${SKILL_NAME}.skill`);
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
function bundledAgentsDir() {
|
|
38
|
+
// __dirname = <pkg>/src/commands; agent definitions live at
|
|
39
|
+
// <pkg>/claude/agents/*.md (README.md excluded — it's docs, not an agent).
|
|
40
|
+
return path.resolve(__dirname, '..', '..', 'claude', 'agents');
|
|
41
|
+
}
|
|
42
|
+
|
|
34
43
|
function claudeSkillsDir() {
|
|
35
44
|
const home = os.homedir();
|
|
36
45
|
if (!home) throw new Error('cannot resolve user home directory');
|
|
37
46
|
return path.join(home, '.claude', 'skills');
|
|
38
47
|
}
|
|
39
48
|
|
|
49
|
+
function claudeAgentsDir() {
|
|
50
|
+
const home = os.homedir();
|
|
51
|
+
if (!home) throw new Error('cannot resolve user home directory');
|
|
52
|
+
return path.join(home, '.claude', 'agents');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Bundled agent-definition files to install — every `*.md` under
|
|
57
|
+
* `claude/agents/` except `README.md` (which is human docs, not an agent
|
|
58
|
+
* definition and would parse as a nameless agent). Returns absolute paths.
|
|
59
|
+
*
|
|
60
|
+
* @returns {string[]}
|
|
61
|
+
*/
|
|
62
|
+
function bundledAgentFiles() {
|
|
63
|
+
const dir = bundledAgentsDir();
|
|
64
|
+
if (!fs.existsSync(dir)) return [];
|
|
65
|
+
return fs.readdirSync(dir)
|
|
66
|
+
.filter((f) => f.endsWith('.md') && f.toLowerCase() !== 'readme.md')
|
|
67
|
+
.sort()
|
|
68
|
+
.map((f) => path.join(dir, f));
|
|
69
|
+
}
|
|
70
|
+
|
|
40
71
|
function rmrf(target) {
|
|
41
72
|
if (!fs.existsSync(target)) return;
|
|
42
73
|
fs.rmSync(target, { recursive: true, force: true });
|
|
43
74
|
}
|
|
44
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Install the bundled onboarding agents into ~/.claude/agents/. Unlike the
|
|
78
|
+
* skill, agents are independent plain-Markdown files, so this never aborts
|
|
79
|
+
* the run: an already-present agent is skipped (with a hint) unless --force,
|
|
80
|
+
* and the rest still install. Dry-run reports without writing.
|
|
81
|
+
*
|
|
82
|
+
* @param {{ force: boolean, dryRun: boolean }} opts
|
|
83
|
+
*/
|
|
84
|
+
function installAgentsForClaude({ force, dryRun }) {
|
|
85
|
+
const agentFiles = bundledAgentFiles();
|
|
86
|
+
if (agentFiles.length === 0) {
|
|
87
|
+
log.info('No bundled agents to install.');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const destDir = claudeAgentsDir();
|
|
92
|
+
log.info(`Agents : ${destDir}`);
|
|
93
|
+
|
|
94
|
+
if (dryRun) {
|
|
95
|
+
for (const src of agentFiles) {
|
|
96
|
+
log.info(`(--dry-run) would install agent ${path.basename(src)} → ${destDir}`);
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
102
|
+
let installed = 0;
|
|
103
|
+
for (const src of agentFiles) {
|
|
104
|
+
const name = path.basename(src);
|
|
105
|
+
const dest = path.join(destDir, name);
|
|
106
|
+
if (fs.existsSync(dest) && !force) {
|
|
107
|
+
log.warn(`agent ${name} already installed — re-run with --force to overwrite`);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
fs.copyFileSync(src, dest);
|
|
111
|
+
installed += 1;
|
|
112
|
+
log.success(`Installed agent ${name} → ${dest}`);
|
|
113
|
+
}
|
|
114
|
+
if (installed > 0) {
|
|
115
|
+
log.info('Claude Code will pick up the agent(s) on next session start.');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
45
119
|
function installForClaude({ force, dryRun }) {
|
|
120
|
+
installSkillForClaude({ force, dryRun });
|
|
121
|
+
installAgentsForClaude({ force, dryRun });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function installSkillForClaude({ force, dryRun }) {
|
|
46
125
|
const zipPath = bundledSkillPath();
|
|
47
126
|
if (!fs.existsSync(zipPath)) {
|
|
48
127
|
log.error(`bundled skill not found at ${zipPath}`);
|
|
@@ -111,5 +190,8 @@ async function installSkill(argv) {
|
|
|
111
190
|
}
|
|
112
191
|
|
|
113
192
|
module.exports = installSkill;
|
|
114
|
-
module.exports.bundledSkillPath
|
|
115
|
-
module.exports.claudeSkillsDir
|
|
193
|
+
module.exports.bundledSkillPath = bundledSkillPath;
|
|
194
|
+
module.exports.claudeSkillsDir = claudeSkillsDir;
|
|
195
|
+
module.exports.bundledAgentsDir = bundledAgentsDir;
|
|
196
|
+
module.exports.claudeAgentsDir = claudeAgentsDir;
|
|
197
|
+
module.exports.bundledAgentFiles = bundledAgentFiles;
|
package/src/commands/link.js
CHANGED
|
@@ -8,6 +8,9 @@ const { createLink, isLink, removeLink, getLinkTarget } = require('../link-utils
|
|
|
8
8
|
const { downloadDependencies } = require('../download');
|
|
9
9
|
const { resolveDependencies } = require('../resolve');
|
|
10
10
|
const { wyvrnFetch } = require('../http-fetch');
|
|
11
|
+
const { resolveBinaryDirRoot } = require('../binary-dir');
|
|
12
|
+
const { readLocalOverlay } = require('../conf');
|
|
13
|
+
const { hasBuildRecipe, DEFAULT_RECIPE } = require('../build/recipe');
|
|
11
14
|
const log = require('../logger');
|
|
12
15
|
|
|
13
16
|
/**
|
|
@@ -37,14 +40,51 @@ function getEffectivePath(entry) {
|
|
|
37
40
|
return entry.path;
|
|
38
41
|
}
|
|
39
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Compute the install-tree subdirectory for a package that declares a build
|
|
45
|
+
* recipe, relative to the package root. Mirrors how `publish` locates the
|
|
46
|
+
* install tree it overlays: `<binaryDirRoot>/wyvrn-<profile>/<installDir>`.
|
|
47
|
+
*
|
|
48
|
+
* The returned path is forward-slash separated and relative — `getEffectivePath`
|
|
49
|
+
* joins it onto the package root to yield the absolute install dir.
|
|
50
|
+
*
|
|
51
|
+
* @param {object} manifest - Parsed wyvrn.json (must have a build recipe).
|
|
52
|
+
* @param {object} config - Resolved config (for defaultProfile fallback).
|
|
53
|
+
* @param {object} argv - Parsed yargs arguments (reads `profile`).
|
|
54
|
+
* @param {string} packagePath - Absolute package root (for the local overlay).
|
|
55
|
+
* @returns {string} Relative, forward-slash install-tree subdir.
|
|
56
|
+
*/
|
|
57
|
+
function installTreeSubdir(manifest, config, argv, packagePath) {
|
|
58
|
+
const overlay = readLocalOverlay(packagePath);
|
|
59
|
+
const binaryDirRoot = resolveBinaryDirRoot({
|
|
60
|
+
localOverlayBinaryDirRoot: overlay.binaryDirRoot,
|
|
61
|
+
});
|
|
62
|
+
const profileName =
|
|
63
|
+
typeof argv.profile === 'string' && argv.profile.length > 0
|
|
64
|
+
? argv.profile
|
|
65
|
+
: config.defaultProfile || 'default';
|
|
66
|
+
const installDir = manifest.build.installDir ?? DEFAULT_RECIPE.installDir;
|
|
67
|
+
return [binaryDirRoot, `wyvrn-${profileName}`, installDir].join('/');
|
|
68
|
+
}
|
|
69
|
+
|
|
40
70
|
/**
|
|
41
71
|
* Register the current directory's package globally for linking.
|
|
42
72
|
* Reads wyvrn.json to get the package name, stores {name: {path, subdir}} in config.
|
|
43
73
|
*
|
|
74
|
+
* When the package declares a build recipe, the registered target is its
|
|
75
|
+
* install tree (`<binaryDirRoot>/wyvrn-<profile>/<installDir>`) rather than the
|
|
76
|
+
* raw source root. That tree carries the installed headers, libraries, and
|
|
77
|
+
* `<name>Config.cmake` at its root — the exact layout a consumer's
|
|
78
|
+
* `find_package(<name> CONFIG)` expects after the link lands in
|
|
79
|
+
* `wyvrn_internal/<name>/`. Linking the source root would bury the CMake
|
|
80
|
+
* config under `build/` and find_package would miss it, so the link must
|
|
81
|
+
* simulate the packaged artefact, not the working tree. An explicit
|
|
82
|
+
* `--subdir` always wins (monorepo: link a sub-package's own tree).
|
|
83
|
+
*
|
|
44
84
|
* @param {string} manifestPath - Path to wyvrn.json
|
|
45
|
-
* @param {
|
|
85
|
+
* @param {object} argv - Parsed yargs arguments (reads `subdir`, `profile`).
|
|
46
86
|
*/
|
|
47
|
-
function registerGlobal(manifestPath,
|
|
87
|
+
function registerGlobal(manifestPath, argv) {
|
|
48
88
|
const manifest = readManifest(manifestPath);
|
|
49
89
|
const packageName = manifest.name || manifest.Name;
|
|
50
90
|
|
|
@@ -58,14 +98,25 @@ function registerGlobal(manifestPath, subdir) {
|
|
|
58
98
|
config.linkedPackages = config.linkedPackages || {};
|
|
59
99
|
|
|
60
100
|
const entry = { path: packagePath };
|
|
61
|
-
|
|
62
|
-
|
|
101
|
+
let installTreeLinked = false;
|
|
102
|
+
if (argv.subdir) {
|
|
103
|
+
entry.subdir = argv.subdir;
|
|
104
|
+
} else if (hasBuildRecipe(manifest)) {
|
|
105
|
+
entry.subdir = installTreeSubdir(manifest, config, argv, packagePath);
|
|
106
|
+
installTreeLinked = true;
|
|
63
107
|
}
|
|
64
108
|
config.linkedPackages[packageName] = entry;
|
|
65
109
|
writeConfig(config);
|
|
66
110
|
|
|
67
111
|
const effectivePath = getEffectivePath(entry);
|
|
68
112
|
log.info(`Registered "${packageName}" → ${effectivePath}`);
|
|
113
|
+
if (installTreeLinked && !fs.existsSync(effectivePath)) {
|
|
114
|
+
log.warn(
|
|
115
|
+
`install tree not found at ${effectivePath}\n` +
|
|
116
|
+
` Build + install it so consumers' find_package(${packageName}) resolves:\n` +
|
|
117
|
+
` wyvrnpm build --all-configs --install`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
69
120
|
}
|
|
70
121
|
|
|
71
122
|
/**
|
|
@@ -261,7 +312,7 @@ async function link(argv) {
|
|
|
261
312
|
log.error(`no wyvrn.json found at ${manifestPath}`);
|
|
262
313
|
process.exit(1);
|
|
263
314
|
}
|
|
264
|
-
registerGlobal(manifestPath, argv
|
|
315
|
+
registerGlobal(manifestPath, argv);
|
|
265
316
|
return;
|
|
266
317
|
}
|
|
267
318
|
|