wyvrnpm 2.0.2 → 2.1.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.
@@ -0,0 +1,242 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { spawn } = require('child_process');
6
+
7
+ const { readConfig } = require('../config');
8
+ const install = require('./install');
9
+ const log = require('../logger');
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Pure helpers (unit-testable — no process spawning, no filesystem writes)
13
+ // ---------------------------------------------------------------------------
14
+
15
+ /**
16
+ * Decide whether `wyvrn install` must run before build.
17
+ *
18
+ * - toolchain-missing : wyvrn_toolchain.cmake does not exist → definitely stale.
19
+ * - lock-newer : wyvrn.lock was modified after the toolchain was written,
20
+ * so the toolchain's package list is out of date.
21
+ * - fresh : no action needed.
22
+ *
23
+ * @param {{ rootDir: string, manifestPath: string }} args
24
+ * @returns {{ stale: boolean, reason: string }}
25
+ */
26
+ function isToolchainStale({ rootDir, manifestPath }) {
27
+ const lockPath = path.join(path.dirname(manifestPath), 'wyvrn.lock');
28
+ const toolchainPath = path.join(rootDir, 'wyvrn_internal', 'wyvrn_toolchain.cmake');
29
+
30
+ if (!fs.existsSync(toolchainPath)) {
31
+ return { stale: true, reason: 'toolchain-missing' };
32
+ }
33
+ if (!fs.existsSync(lockPath)) {
34
+ return { stale: false, reason: 'no-lock' };
35
+ }
36
+
37
+ const lockMtime = fs.statSync(lockPath).mtimeMs;
38
+ const toolchainMtime = fs.statSync(toolchainPath).mtimeMs;
39
+ if (lockMtime > toolchainMtime) {
40
+ return { stale: true, reason: 'lock-newer-than-toolchain' };
41
+ }
42
+ return { stale: false, reason: 'fresh' };
43
+ }
44
+
45
+ /**
46
+ * Resolve the configure preset name from CLI override or profile convention.
47
+ *
48
+ * @param {{ presetOverride?: string, profileName: string }} args
49
+ * @returns {string}
50
+ */
51
+ function resolvePresetName({ presetOverride, profileName }) {
52
+ if (presetOverride) return presetOverride;
53
+ return `wyvrn-${profileName}`;
54
+ }
55
+
56
+ /**
57
+ * Compute the build directory for a preset, matching the pattern emitted
58
+ * by [src/toolchain/presets.js](../toolchain/presets.js) — `${sourceDir}/build/<preset>`.
59
+ *
60
+ * @param {{ rootDir: string, presetName: string }} args
61
+ * @returns {string}
62
+ */
63
+ function resolveBinaryDir({ rootDir, presetName }) {
64
+ return path.join(rootDir, 'build', presetName);
65
+ }
66
+
67
+ /**
68
+ * Args passed to `cmake` for the configure step.
69
+ * @param {{ presetName: string }} args
70
+ * @returns {string[]}
71
+ */
72
+ function buildConfigureArgs({ presetName }) {
73
+ return ['--preset', presetName];
74
+ }
75
+
76
+ /**
77
+ * Args passed to `cmake` for the build step.
78
+ *
79
+ * @param {{
80
+ * binaryDir: string,
81
+ * config: string,
82
+ * target?: string,
83
+ * verbose?: boolean,
84
+ * passthru?: string[],
85
+ * }} args
86
+ * @returns {string[]}
87
+ */
88
+ function buildBuildArgs({ binaryDir, config, target, verbose, passthru = [] }) {
89
+ const args = ['--build', binaryDir, '--config', config];
90
+ if (verbose) args.push('--verbose');
91
+ if (target) args.push('--target', target);
92
+ if (passthru.length > 0) args.push('--', ...passthru);
93
+ return args;
94
+ }
95
+
96
+ /**
97
+ * Args passed to `cmake --install` for the install step.
98
+ *
99
+ * @param {{
100
+ * binaryDir: string,
101
+ * config: string,
102
+ * prefix?: string, // override CMAKE_INSTALL_PREFIX at install time
103
+ * }} args
104
+ * @returns {string[]}
105
+ */
106
+ function buildInstallArgs({ binaryDir, config, prefix }) {
107
+ const args = ['--install', binaryDir, '--config', config];
108
+ if (prefix) args.push('--prefix', prefix);
109
+ return args;
110
+ }
111
+
112
+ // ---------------------------------------------------------------------------
113
+ // Spawn wrapper
114
+ // ---------------------------------------------------------------------------
115
+
116
+ /**
117
+ * Run `cmake` with the given args. Inherits stdio for real-time output.
118
+ * Rejects if the process exits non-zero or fails to spawn.
119
+ *
120
+ * @param {string[]} args
121
+ * @returns {Promise<void>}
122
+ */
123
+ function runCmake(args) {
124
+ return new Promise((resolve, reject) => {
125
+ const proc = spawn('cmake', args, { stdio: 'inherit' });
126
+ proc.on('error', (err) => {
127
+ reject(new Error(
128
+ `Failed to spawn cmake: ${err.message}\n` +
129
+ ' Is CMake installed and on PATH?',
130
+ ));
131
+ });
132
+ proc.on('close', (code) => {
133
+ if (code === 0) resolve();
134
+ else reject(new Error(`cmake ${args.join(' ')} exited with code ${code}`));
135
+ });
136
+ });
137
+ }
138
+
139
+ // ---------------------------------------------------------------------------
140
+ // Command entry
141
+ // ---------------------------------------------------------------------------
142
+
143
+ /**
144
+ * `wyvrnpm build` — configure + build via the generated CMake preset.
145
+ *
146
+ * Defaults:
147
+ * - preset : `wyvrn-<profileName>` (from --profile or config.defaultProfile)
148
+ * - config : Release
149
+ * - auto-install when toolchain is missing / older than wyvrn.lock
150
+ *
151
+ * @param {object} argv
152
+ */
153
+ async function build(argv) {
154
+ const rootDir = path.resolve(argv.root);
155
+ const manifestPath = path.resolve(argv.manifest);
156
+
157
+ const config = readConfig();
158
+ const profileName = argv.profile ?? config.defaultProfile ?? 'default';
159
+ const presetName = resolvePresetName({ presetOverride: argv.preset, profileName });
160
+ const buildConfig = argv.config ?? 'Release';
161
+
162
+ // ── Stale check / auto-install ────────────────────────────────────────────
163
+ // `--auto-install` gates the "run `wyvrnpm install` first when the
164
+ // toolchain is missing or older than wyvrn.lock" behaviour. The separate
165
+ // `--install` flag below runs `cmake --install` AFTER build; the two are
166
+ // deliberately distinct concepts.
167
+ const staleness = isToolchainStale({ rootDir, manifestPath });
168
+ if (staleness.stale) {
169
+ if (argv.autoInstall === false) {
170
+ log.error(
171
+ `toolchain is stale (${staleness.reason}) and --no-auto-install was passed.\n` +
172
+ ' Run: wyvrnpm install',
173
+ );
174
+ process.exit(1);
175
+ }
176
+ log.info(`Toolchain ${staleness.reason} — running install first`);
177
+ await install({
178
+ manifest: argv.manifest,
179
+ root: argv.root,
180
+ profile: argv.profile,
181
+ source: argv.source,
182
+ platform: argv.platform ?? 'win_x64',
183
+ timeout: argv.timeout ?? 300,
184
+ });
185
+ }
186
+
187
+ const binaryDir = resolveBinaryDir({ rootDir, presetName });
188
+
189
+ // ── Clean ─────────────────────────────────────────────────────────────────
190
+ if (argv.clean && fs.existsSync(binaryDir)) {
191
+ log.info(`Removing ${binaryDir}`);
192
+ fs.rmSync(binaryDir, { recursive: true, force: true });
193
+ }
194
+
195
+ // ── Configure ─────────────────────────────────────────────────────────────
196
+ const configureArgs = buildConfigureArgs({ presetName });
197
+ log.info(`cmake ${configureArgs.join(' ')}`);
198
+ await runCmake(configureArgs);
199
+
200
+ // ── Build ─────────────────────────────────────────────────────────────────
201
+ const buildArgs = buildBuildArgs({
202
+ binaryDir,
203
+ config: buildConfig,
204
+ target: argv.target,
205
+ verbose: argv.verbose,
206
+ passthru: argv['--'] ?? [],
207
+ });
208
+ log.info(`cmake ${buildArgs.join(' ')}`);
209
+ await runCmake(buildArgs);
210
+
211
+ // ── Install (optional) ────────────────────────────────────────────────────
212
+ // `--install` runs `cmake --install <binaryDir>` after the build. Prefix
213
+ // defaults to whatever was baked into the cache at configure time; pass
214
+ // `--install-prefix` to override without reconfiguring.
215
+ if (argv.install) {
216
+ const installArgs = buildInstallArgs({
217
+ binaryDir,
218
+ config: buildConfig,
219
+ prefix: argv.installPrefix,
220
+ });
221
+ log.info(`cmake ${installArgs.join(' ')}`);
222
+ await runCmake(installArgs);
223
+ }
224
+
225
+ const suffix = argv.install
226
+ ? ` + installed${argv.installPrefix ? ` to ${argv.installPrefix}` : ''}`
227
+ : '';
228
+ log.success(`Build complete: ${binaryDir} (${buildConfig})${suffix}`);
229
+ }
230
+
231
+ module.exports = build;
232
+ module.exports.default = build;
233
+
234
+ // Exported for unit tests
235
+ module.exports._helpers = {
236
+ isToolchainStale,
237
+ resolvePresetName,
238
+ resolveBinaryDir,
239
+ buildConfigureArgs,
240
+ buildBuildArgs,
241
+ buildInstallArgs,
242
+ };
@@ -3,16 +3,22 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
+ const { clearBuildCache, getBuildCacheRoot } = require('../build/cache');
7
+ const log = require('../logger');
8
+
6
9
  /**
7
- * Removes the wyvrn_internal/ package cache directory and the wyvrn.lock file.
10
+ * Removes the project's `wyvrn_internal/` package cache and `wyvrn.lock` file.
11
+ * With `--build-cache`, also clears the machine-wide source-build cache
12
+ * under `%LOCALAPPDATA%\wyvrnpm\bc\` (shared across projects).
8
13
  *
9
- * @param {object} argv - Parsed yargs arguments.
10
- * @param {string} argv.root - Project root directory.
11
- * @param {string} argv.manifest - Path to the manifest file (used to locate wyvrn.lock).
14
+ * @param {object} argv
15
+ * @param {string} argv.root Project root directory.
16
+ * @param {string} argv.manifest Path to the manifest file (used to locate wyvrn.lock).
17
+ * @param {boolean} [argv.buildCache] When true, also nukes the source-build cache.
12
18
  * @returns {Promise<void>}
13
19
  */
14
20
  async function clean(argv) {
15
- const rootDir = path.resolve(argv.root);
21
+ const rootDir = path.resolve(argv.root);
16
22
  const razerDir = path.join(rootDir, 'wyvrn_internal');
17
23
  const lockPath = path.join(path.dirname(path.resolve(argv.manifest)), 'wyvrn.lock');
18
24
 
@@ -20,20 +26,30 @@ async function clean(argv) {
20
26
 
21
27
  if (fs.existsSync(razerDir)) {
22
28
  fs.rmSync(razerDir, { recursive: true, force: true });
23
- console.log(`[wyvrn] Removed ${razerDir}`);
29
+ log.info(`Removed ${razerDir}`);
24
30
  removedAnything = true;
25
31
  } else {
26
- console.log(`[wyvrn] Nothing to clean at ${razerDir}`);
32
+ log.info(`Nothing to clean at ${razerDir}`);
27
33
  }
28
34
 
29
35
  if (fs.existsSync(lockPath)) {
30
36
  fs.unlinkSync(lockPath);
31
- console.log(`[wyvrn] Removed ${lockPath}`);
37
+ log.info(`Removed ${lockPath}`);
32
38
  removedAnything = true;
33
39
  }
34
40
 
41
+ if (argv.buildCache) {
42
+ const removed = clearBuildCache();
43
+ if (removed) {
44
+ log.info(`Removed source-build cache ${removed}`);
45
+ removedAnything = true;
46
+ } else {
47
+ log.info(`No source-build cache at ${getBuildCacheRoot()}`);
48
+ }
49
+ }
50
+
35
51
  if (!removedAnything) {
36
- console.log('[wyvrn] Nothing to clean.');
52
+ log.info('Nothing to clean.');
37
53
  }
38
54
  }
39
55
 
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const { readConfig, writeConfig, getConfigPath } = require('../config');
4
+ const log = require('../logger');
4
5
 
5
6
  /** wyvrnpm configure list */
6
7
  function list() {
@@ -47,14 +48,14 @@ function addSource(argv) {
47
48
  const idx = config[key].findIndex((s) => s.name === name);
48
49
  if (idx !== -1) {
49
50
  config[key][idx] = entry;
50
- console.log(`[wyvrn] Updated ${kind} source "${name}"`);
51
+ log.info(`Updated ${kind} source "${name}"`);
51
52
  } else {
52
53
  config[key].push(entry);
53
- console.log(`[wyvrn] Added ${kind} source "${name}"`);
54
+ log.info(`Added ${kind} source "${name}"`);
54
55
  }
55
56
 
56
57
  writeConfig(config);
57
- console.log(`[wyvrn] Saved to ${getConfigPath()}`);
58
+ log.info(`Saved to ${getConfigPath()}`);
58
59
  }
59
60
 
60
61
  /**
@@ -69,12 +70,12 @@ function removeSource(argv) {
69
70
  config[key] = config[key].filter((s) => s.name !== name);
70
71
 
71
72
  if (config[key].length === before) {
72
- console.error(`[wyvrn] Error: No ${kind} source named "${name}"`);
73
+ log.error(`No ${kind} source named "${name}"`);
73
74
  process.exit(1);
74
75
  }
75
76
 
76
77
  writeConfig(config);
77
- console.log(`[wyvrn] Removed ${kind} source "${name}"`);
78
+ log.info(`Removed ${kind} source "${name}"`);
78
79
  }
79
80
 
80
81
  module.exports = { list, addSource, removeSource };
@@ -3,6 +3,7 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const { defaultManifest, writeManifest } = require('../manifest');
6
+ const log = require('../logger');
6
7
 
7
8
  /**
8
9
  * Initialises a new wyvrn.json manifest in the project root.
@@ -18,7 +19,7 @@ async function init(argv) {
18
19
  const rootDir = path.resolve(argv.root);
19
20
 
20
21
  if (fs.existsSync(manifestPath)) {
21
- console.log(`[wyvrn] Manifest already exists at ${manifestPath}`);
22
+ log.info(`Manifest already exists at ${manifestPath}`);
22
23
  return;
23
24
  }
24
25
 
@@ -27,7 +28,7 @@ async function init(argv) {
27
28
 
28
29
  writeManifest(manifestPath, manifest);
29
30
 
30
- console.log(`[wyvrn] Created manifest at ${manifestPath}`);
31
+ log.success(`Created manifest at ${manifestPath}`);
31
32
  }
32
33
 
33
34
  module.exports = init;
@@ -8,6 +8,8 @@ const { resolveDependencies } = require('../resolv
8
8
  const { downloadDependencies } = require('../download');
9
9
  const { readConfig } = require('../config');
10
10
  const { loadProfile, hashProfile, formatProfile, mergeProfile } = require('../profile');
11
+ const { generateToolchain, generateCMakePresets } = require('../toolchain');
12
+ const log = require('../logger');
11
13
 
12
14
  /**
13
15
  * Resolves and downloads all dependencies declared in the manifest, then writes
@@ -23,6 +25,15 @@ const { loadProfile, hashProfile, formatProfile, mergeProfile } = require('../pr
23
25
  async function install(argv) {
24
26
  const manifestPath = path.resolve(argv.manifest);
25
27
  const rootDir = path.resolve(argv.root);
28
+ const buildMode = argv.build ?? 'never';
29
+
30
+ if (buildMode === 'always') {
31
+ log.error(
32
+ '--build=always is not implemented (requires PLAN-BUILD-MISSING phase 3).\n' +
33
+ ' Use --build=never (default) or --build=missing.',
34
+ );
35
+ process.exit(1);
36
+ }
26
37
 
27
38
  // ── Auth / sources ────────────────────────────────────────────────────────
28
39
  let packageSources;
@@ -32,13 +43,13 @@ async function install(argv) {
32
43
  const config = readConfig();
33
44
  packageSources = config.installSources.map((s) => s.url);
34
45
  if (packageSources.length > 0) {
35
- console.log(`[wyvrn] Using ${packageSources.length} source(s) from config`);
46
+ log.info(`Using ${packageSources.length} source(s) from config`);
36
47
  }
37
48
  }
38
49
 
39
50
  if (packageSources.length === 0) {
40
- console.warn(
41
- '[wyvrn] Warning: no sources configured. ' +
51
+ log.warn(
52
+ 'no sources configured. ' +
42
53
  'Pass --source <url> or run: wyvrnpm configure add-source --kind install ...',
43
54
  );
44
55
  }
@@ -48,7 +59,7 @@ async function install(argv) {
48
59
  const profileName = argv.profile ?? config.defaultProfile ?? 'default';
49
60
  const { profile: baseProfile, fromFile } = loadProfile(profileName);
50
61
 
51
- console.log(`[wyvrn] Profile: "${profileName}"${fromFile ? '' : ' (auto-detected)'}`);
62
+ log.info(`Profile: "${profileName}"${fromFile ? '' : ' (auto-detected)'}`);
52
63
  console.log(formatProfile(baseProfile).split('\n').map((l) => ` ${l}`).join('\n'));
53
64
  console.log(` Profile hash : ${hashProfile(baseProfile)}`);
54
65
 
@@ -63,9 +74,9 @@ async function install(argv) {
63
74
  const ver = typeof entry === 'string' ? entry : entry.version;
64
75
  if (ver) lockedVersions.set(name, ver);
65
76
  }
66
- console.log(`[wyvrn] Lock file found — ${lockedVersions.size} pinned package(s)`);
77
+ log.info(`Lock file found — ${lockedVersions.size} pinned package(s)`);
67
78
  } catch {
68
- console.warn('[wyvrn] Warning: could not read wyvrn.lock, resolving from scratch');
79
+ log.warn('could not read wyvrn.lock, resolving from scratch');
69
80
  }
70
81
  }
71
82
 
@@ -80,8 +91,8 @@ async function install(argv) {
80
91
  Object.entries(normalizedDeps).map(([name, d]) => [name, d.version]),
81
92
  );
82
93
 
83
- console.log(
84
- `[wyvrn] Installing dependencies for "${manifest.name}" (platform: ${argv.platform})`,
94
+ log.info(
95
+ `Installing dependencies for "${manifest.name}" (platform: ${argv.platform})`,
85
96
  );
86
97
 
87
98
  const resolvedDeps = await resolveDependencies(
@@ -102,8 +113,8 @@ async function install(argv) {
102
113
  const profileHash = hashProfile(effectiveProfile);
103
114
 
104
115
  if (Object.keys(depOverrides).length > 0) {
105
- console.log(
106
- `[wyvrn] ${name}: settings override → profileHash ${profileHash}` +
116
+ log.info(
117
+ `${name}: settings override → profileHash ${profileHash}` +
107
118
  ` (${JSON.stringify(depOverrides)})`,
108
119
  );
109
120
  }
@@ -115,9 +126,11 @@ async function install(argv) {
115
126
 
116
127
  // Extract auth from the first install source (if any)
117
128
  const firstInstallSrc = config.installSources?.[0];
118
- const authOptions = {
129
+ const downloadOptions = {
119
130
  awsProfile: firstInstallSrc?.profile ?? undefined,
120
131
  token: firstInstallSrc?.token ?? undefined,
132
+ buildMode,
133
+ profileName,
121
134
  };
122
135
 
123
136
  const lockEntries = await downloadDependencies(
@@ -127,7 +140,7 @@ async function install(argv) {
127
140
  razerDir,
128
141
  fetch,
129
142
  argv.timeout,
130
- authOptions,
143
+ downloadOptions,
131
144
  );
132
145
 
133
146
  // ── Write v2 lock file ────────────────────────────────────────────────────
@@ -147,10 +160,44 @@ async function install(argv) {
147
160
  };
148
161
 
149
162
  fs.writeFileSync(lockPath, JSON.stringify(lockData, null, 2) + '\n', 'utf8');
150
- console.log(`[wyvrn] Lock file written to ${lockPath}`);
163
+ log.info(`Lock file written to ${lockPath}`);
164
+
165
+ // ── CMake toolchain generation ────────────────────────────────────────────
166
+ const packageNames = [...resolvedDeps.keys()].sort();
167
+ const { toolchain } = generateToolchain({
168
+ destDir: razerDir,
169
+ profile: baseProfile,
170
+ profileName,
171
+ profileHash: hashProfile(baseProfile),
172
+ packageNames,
173
+ });
174
+ log.info(`CMake toolchain written to ${toolchain}`);
175
+
176
+ // ── CMakePresets.json emission ────────────────────────────────────────────
177
+ const toolchainRelPath = path
178
+ .relative(rootDir, toolchain)
179
+ .replace(/\\/g, '/');
180
+ const presetResult = generateCMakePresets({
181
+ projectRoot: rootDir,
182
+ profileName,
183
+ profileHash: hashProfile(baseProfile),
184
+ toolchainRelPath,
185
+ });
186
+ if (presetResult.action === 'skipped') {
187
+ log.warn(
188
+ 'could not write presets — both CMakePresets.json and ' +
189
+ 'CMakeUserPresets.json exist and are user-owned. Add this to your presets:\n' +
190
+ ` { "name": "wyvrn-${profileName}", "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": "\${sourceDir}/${toolchainRelPath}" } }`,
191
+ );
192
+ } else {
193
+ log.info(
194
+ `CMake presets ${presetResult.action}: ${presetResult.path}` +
195
+ (presetResult.isUser ? ' (user presets — CMakePresets.json is user-owned)' : ''),
196
+ );
197
+ }
151
198
 
152
199
  const count = resolvedDeps.size;
153
- console.log(`[wyvrn] Done — ${count} package${count !== 1 ? 's' : ''} installed.`);
200
+ log.success(`Done — ${count} package${count !== 1 ? 's' : ''} installed.`);
154
201
  }
155
202
 
156
203
  module.exports = install;
@@ -7,6 +7,7 @@ const { readManifest } = require('../manifest');
7
7
  const { createLink, isLink, removeLink, getLinkTarget } = require('../link-utils');
8
8
  const { downloadDependencies } = require('../download');
9
9
  const { resolveDependencies } = require('../resolve');
10
+ const log = require('../logger');
10
11
 
11
12
  /**
12
13
  * Normalizes a linked package entry to object form.
@@ -47,7 +48,7 @@ function registerGlobal(manifestPath, subdir) {
47
48
  const packageName = manifest.name || manifest.Name;
48
49
 
49
50
  if (!packageName) {
50
- console.error('[wyvrn] Error: wyvrn.json must have a "name" field');
51
+ log.error('wyvrn.json must have a "name" field');
51
52
  process.exit(1);
52
53
  }
53
54
 
@@ -63,7 +64,7 @@ function registerGlobal(manifestPath, subdir) {
63
64
  writeConfig(config);
64
65
 
65
66
  const effectivePath = getEffectivePath(entry);
66
- console.log(`[wyvrn] Registered "${packageName}" → ${effectivePath}`);
67
+ log.info(`Registered "${packageName}" → ${effectivePath}`);
67
68
  }
68
69
 
69
70
  /**
@@ -77,7 +78,7 @@ function linkToProject(name, sourcePath, rootDir) {
77
78
  const absoluteSource = path.resolve(sourcePath);
78
79
 
79
80
  if (!fs.existsSync(absoluteSource)) {
80
- console.error(`[wyvrn] Error: source path does not exist: ${absoluteSource}`);
81
+ log.error(`source path does not exist: ${absoluteSource}`);
81
82
  process.exit(1);
82
83
  }
83
84
 
@@ -106,7 +107,7 @@ function linkToProject(name, sourcePath, rootDir) {
106
107
  lockData.generatedAt = new Date().toISOString();
107
108
  fs.writeFileSync(lockPath, JSON.stringify(lockData, null, 2) + '\n', 'utf8');
108
109
 
109
- console.log(`[wyvrn] Linked: ${name} → ${absoluteSource}`);
110
+ log.info(`Linked: ${name} → ${absoluteSource}`);
110
111
  }
111
112
 
112
113
  /**
@@ -122,18 +123,18 @@ async function unlinkFromProject(name, rootDir, restore, argv) {
122
123
  const linkPath = path.join(razerDir, name);
123
124
 
124
125
  if (!fs.existsSync(linkPath)) {
125
- console.error(`[wyvrn] Error: package "${name}" is not installed`);
126
+ log.error(`package "${name}" is not installed`);
126
127
  process.exit(1);
127
128
  }
128
129
 
129
130
  if (!isLink(linkPath)) {
130
- console.error(`[wyvrn] Error: package "${name}" is not a linked package`);
131
+ log.error(`package "${name}" is not a linked package`);
131
132
  process.exit(1);
132
133
  }
133
134
 
134
135
  // Remove the symlink
135
136
  removeLink(linkPath);
136
- console.log(`[wyvrn] Unlinked: ${name}`);
137
+ log.info(`Unlinked: ${name}`);
137
138
 
138
139
  // Update the lock file
139
140
  const lockPath = path.join(rootDir, 'wyvrn.lock');
@@ -161,7 +162,7 @@ async function unlinkFromProject(name, rootDir, restore, argv) {
161
162
  : rawDeps;
162
163
 
163
164
  if (!deps[name]) {
164
- console.warn(`[wyvrn] Warning: "${name}" is not in manifest dependencies, cannot restore`);
165
+ log.warn(`"${name}" is not in manifest dependencies, cannot restore`);
165
166
  return;
166
167
  }
167
168
 
@@ -175,7 +176,7 @@ async function unlinkFromProject(name, rootDir, restore, argv) {
175
176
  }
176
177
 
177
178
  if (packageSources.length === 0) {
178
- console.warn('[wyvrn] Warning: no sources configured, cannot restore package');
179
+ log.warn('no sources configured, cannot restore package');
179
180
  return;
180
181
  }
181
182
 
@@ -223,11 +224,11 @@ function listLinked() {
223
224
  const entries = Object.entries(linked);
224
225
 
225
226
  if (entries.length === 0) {
226
- console.log('[wyvrn] No globally registered packages');
227
+ log.info('No globally registered packages');
227
228
  return;
228
229
  }
229
230
 
230
- console.log('[wyvrn] Globally registered packages:');
231
+ log.info('Globally registered packages:');
231
232
  for (const [name, rawEntry] of entries) {
232
233
  const entry = normalizeLinkedEntry(rawEntry);
233
234
  const effectivePath = getEffectivePath(entry);
@@ -256,7 +257,7 @@ async function link(argv) {
256
257
  // wyvrnpm link (no args) - register current package globally
257
258
  if (!argv.name) {
258
259
  if (!fs.existsSync(manifestPath)) {
259
- console.error(`[wyvrn] Error: no wyvrn.json found at ${manifestPath}`);
260
+ log.error(`no wyvrn.json found at ${manifestPath}`);
260
261
  process.exit(1);
261
262
  }
262
263
  registerGlobal(manifestPath, argv.subdir);
@@ -278,9 +279,11 @@ async function link(argv) {
278
279
  const linked = config.linkedPackages || {};
279
280
 
280
281
  if (!linked[argv.name]) {
281
- console.error(`[wyvrn] Error: "${argv.name}" is not registered globally`);
282
- console.error('[wyvrn] Run "wyvrnpm link" in the package directory first, or use:');
283
- console.error(`[wyvrn] wyvrnpm link ${argv.name} <path>`);
282
+ log.error(
283
+ `"${argv.name}" is not registered globally\n` +
284
+ `Run "wyvrnpm link" in the package directory first, or use:\n` +
285
+ ` wyvrnpm link ${argv.name} <path>`,
286
+ );
284
287
  process.exit(1);
285
288
  }
286
289