@vltpkg/cli-sdk 1.5.0 → 1.6.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.
Files changed (59) hide show
  1. package/dist/commands/build.d.ts +8 -3
  2. package/dist/commands/build.js +31 -3
  3. package/dist/commands/ci.d.ts +1 -1
  4. package/dist/commands/create.d.ts +2 -1
  5. package/dist/commands/create.js +2 -3
  6. package/dist/commands/exec.d.ts +2 -1
  7. package/dist/commands/exec.js +2 -3
  8. package/dist/commands/init.js +5 -2
  9. package/dist/commands/install/reporter.d.ts +1 -1
  10. package/dist/commands/install/reporter.js +45 -5
  11. package/dist/commands/install.d.ts +21 -3
  12. package/dist/commands/install.js +84 -30
  13. package/dist/commands/list.js +5 -1
  14. package/dist/commands/query.js +5 -1
  15. package/dist/commands/setup.d.ts +28 -2
  16. package/dist/commands/setup.js +185 -3
  17. package/dist/commands/skills.js +2 -3
  18. package/dist/commands/token.js +1 -1
  19. package/dist/commands/uninstall.d.ts +10 -3
  20. package/dist/commands/uninstall.js +28 -2
  21. package/dist/commands/update.d.ts +1 -1
  22. package/dist/config/definition.d.ts +32 -0
  23. package/dist/config/definition.js +60 -0
  24. package/dist/config/index.d.ts +26 -3
  25. package/dist/config/index.js +149 -7
  26. package/dist/global-bin-dir.d.ts +37 -0
  27. package/dist/global-bin-dir.js +138 -0
  28. package/dist/global-project.d.ts +18 -0
  29. package/dist/global-project.js +67 -0
  30. package/dist/global-state.d.ts +24 -0
  31. package/dist/global-state.js +94 -0
  32. package/dist/global.d.ts +78 -0
  33. package/dist/global.js +409 -0
  34. package/dist/index.js +7 -2
  35. package/dist/install-receipt.d.ts +40 -0
  36. package/dist/install-receipt.js +69 -0
  37. package/dist/output.js +13 -0
  38. package/dist/pack-tarball.d.ts +3 -1
  39. package/dist/pack-tarball.js +29 -19
  40. package/dist/parse-add-remove-args.js +2 -14
  41. package/dist/query-auth.d.ts +3 -2
  42. package/dist/query-auth.js +6 -4
  43. package/dist/reload-config.js +2 -1
  44. package/dist/require-registry.d.ts +7 -0
  45. package/dist/require-registry.js +13 -1
  46. package/dist/self-update.d.ts +74 -0
  47. package/dist/self-update.js +401 -0
  48. package/dist/shell-setup/env.d.ts +9 -0
  49. package/dist/shell-setup/env.js +25 -0
  50. package/dist/shell-setup/index.d.ts +69 -0
  51. package/dist/shell-setup/index.js +213 -0
  52. package/dist/shell-setup/posix.d.ts +36 -0
  53. package/dist/shell-setup/posix.js +158 -0
  54. package/dist/shell-setup/windows.d.ts +39 -0
  55. package/dist/shell-setup/windows.js +151 -0
  56. package/dist/shell.d.ts +18 -0
  57. package/dist/shell.js +110 -0
  58. package/dist/telemetry.js +8 -1
  59. package/package.json +30 -28
@@ -8,6 +8,10 @@ import { registrySelectionFields } from "../config/merge-layers.js";
8
8
  import { commandUsage } from "../config/usage.js";
9
9
  import { normalizeRegistryURL } from "../require-registry.js";
10
10
  import { stdout } from "../output.js";
11
+ import { resolveGlobalBinDir } from "../global-bin-dir.js";
12
+ import { detectShell, prettyPath, shellPathCommand, } from "../shell.js";
13
+ import { applyPathSetup, ask as askLine, pathSetupChanged, pathSetupFiles, pathSetupLines, planPathSetup, rcList, sourceCommand, } from "../shell-setup/index.js";
14
+ import { editPathHint } from "../shell-setup/windows.js";
11
15
  /** Where users sign up for / log into a vlt.io account. */
12
16
  export const VLT_SIGNUP_URL = 'https://www.vlt.io';
13
17
  /** Base URL that per-account registries live under. */
@@ -58,8 +62,47 @@ const accountAuthError = (account, er) => (isErrorWithCause(er) &&
58
62
  `organization at ${VLT_SIGNUP_URL}, then run \`vlt setup\` again.`,
59
63
  ].join('\n'), { code: 'ECONFIG', cause: er })
60
64
  : asError(er);
65
+ const pathView = (r, wizard = false) => {
66
+ if (r.status === 'failed') {
67
+ return `⚠️ Could not add the global bin folder to your PATH: ${r.message}`;
68
+ }
69
+ const dir = prettyPath(r.dir);
70
+ const rcs = rcList(r.files);
71
+ // the command lasts one session
72
+ const keep = process.platform === 'win32' ?
73
+ [` To keep it: ${editPathHint}`]
74
+ : [];
75
+ switch (r.status) {
76
+ case 'added':
77
+ return pathSetupLines(r, r.source).join('\n');
78
+ case 'unchanged':
79
+ return [
80
+ `✅ ${dir} is already added to your PATH in ${rcs}`,
81
+ ` Open a new terminal, or run: ${r.source}`,
82
+ ].join('\n');
83
+ case 'in-path':
84
+ return wizard ? '' : `✅ ${dir} is already on your PATH`;
85
+ case 'disabled':
86
+ return [
87
+ `ℹ️ no-modify-path is set, so nothing was changed. To add ${dir} to your PATH, run:`,
88
+ ` ${r.command}`,
89
+ ...keep,
90
+ ].join('\n');
91
+ case 'cancelled':
92
+ if (wizard) {
93
+ return `ℹ️ Skipped. Run \`vlt setup --path\` later to add ${dir} to your PATH.`;
94
+ }
95
+ return [
96
+ `Nothing changed. To add ${dir} to your PATH, run:`,
97
+ ` ${r.command}`,
98
+ ...keep,
99
+ ].join('\n');
100
+ }
101
+ };
61
102
  export const views = {
62
103
  human: (result) => {
104
+ if (!('account' in result))
105
+ return pathView(result.path);
63
106
  const lines = [
64
107
  `Configured ${Object.keys(result.registries).length} registry ${Object.keys(result.registries).length === 1 ?
65
108
  'alias'
@@ -71,13 +114,19 @@ export const views = {
71
114
  if (result.shadowedByProject) {
72
115
  lines.push('', `Note: this project's vlt.json configures its own registries, and`, 'project config takes precedence. Re-run with `--config=project`', 'to configure this project instead.');
73
116
  }
117
+ const path = result.path && pathView(result.path, true);
118
+ if (path)
119
+ lines.push('', path);
74
120
  lines.push('', 'You can now install packages, e.g.:', ' vlt install <pkg>');
75
121
  return lines.join('\n');
76
122
  },
77
123
  };
78
124
  export const usage = () => commandUsage({
79
125
  command: 'setup',
80
- usage: '[<account>]',
126
+ usage: [
127
+ '[<account>]',
128
+ '--path [--yes] [--global-bin-dir=<path>]',
129
+ ],
81
130
  description: `Configure the registry aliases for your vlt.io account so
82
131
  that \`vlt install\` and \`vlx\` work out of the box.
83
132
 
@@ -85,7 +134,19 @@ export const usage = () => commandUsage({
85
134
  authenticates against your account, stores the token
86
135
  for both the \`npm\` and \`main\` registries, and writes
87
136
  those aliases (and any additional aliases you add) to
88
- your user \`vlt.json\`.`,
137
+ your user \`vlt.json\`.
138
+
139
+ In a terminal, if the global bin folder isn't on
140
+ your \`PATH\` or set up yet, it ends by asking
141
+ \`Setup PATH for global installs? (Y/n)\`: yes adds
142
+ it (same as \`--path\`), no skips it. Run
143
+ \`vlt setup --path\` later to do just that step.
144
+
145
+ With \`--path\`, add the global bin folder
146
+ (\`--global-bin-dir\`) to your \`PATH\` instead: writes
147
+ \`<global-dir>/env\` and a marked section that loads
148
+ it into your shell config. On Windows it adds it to
149
+ your user \`Path\` (\`HKCU\\Environment\`).`,
89
150
  options: {
90
151
  config: {
91
152
  value: '<user | project>',
@@ -99,11 +160,129 @@ export const usage = () => commandUsage({
99
160
  },
100
161
  yes: {
101
162
  description: 'Skip interactive prompts (requires <account> and does not ' +
102
- 'open a browser for authentication).',
163
+ 'open a browser for authentication). With --path: edit ' +
164
+ 'without asking. Without --path: skips the PATH prompt.',
165
+ },
166
+ path: {
167
+ description: 'Only add the global bin folder to your PATH via your ' +
168
+ 'shell config (user Path on Windows).',
169
+ },
170
+ 'global-bin-dir': {
171
+ value: '<path>',
172
+ description: 'The folder to add to your PATH.',
103
173
  },
104
174
  },
105
175
  });
176
+ const runPathSetup = async (o) => {
177
+ const root = o.globalDir;
178
+ const bin = await resolveGlobalBinDir({
179
+ projectRoot: root,
180
+ globalBinDir: o.globalBinDir,
181
+ });
182
+ const shell = detectShell();
183
+ const base = {
184
+ dir: bin.dir,
185
+ shell,
186
+ files: [],
187
+ backups: [],
188
+ command: shellPathCommand(shell, bin.dir),
189
+ source: sourceCommand(shell, root, bin.dir),
190
+ };
191
+ if (bin.inPath) {
192
+ return { ...base, status: 'in-path' };
193
+ }
194
+ // scripted installers must not fail
195
+ if (o.noModifyPath) {
196
+ return { ...base, status: 'disabled' };
197
+ }
198
+ const opts = { globalDir: root, dir: bin.dir, shell };
199
+ let plan = await planPathSetup(opts);
200
+ const files = pathSetupFiles(plan);
201
+ if (!pathSetupChanged(plan)) {
202
+ return { ...base, status: 'unchanged', files };
203
+ }
204
+ if (!o.yes) {
205
+ if (!process.stdin.isTTY) {
206
+ throw error('Not a terminal: pass --yes to add it to your PATH', {
207
+ code: 'EUSAGE',
208
+ });
209
+ }
210
+ const answer = await (o.ask ?? askLine)(o.question({ dir: prettyPath(bin.dir), rcs: rcList(files) })).catch((er) => {
211
+ // Ctrl-C
212
+ if (asError(er).name !== 'AbortError')
213
+ throw er;
214
+ process.stdout.write('\n');
215
+ return 'n';
216
+ });
217
+ if (/^n/i.test(answer)) {
218
+ return { ...base, status: 'cancelled', files };
219
+ }
220
+ // rc files may have changed while asking
221
+ plan = await planPathSetup(opts);
222
+ }
223
+ const r = await applyPathSetup(plan);
224
+ return {
225
+ ...base,
226
+ status: 'added',
227
+ files: r.files,
228
+ backups: r.backups,
229
+ };
230
+ };
231
+ const setupPath = async (conf, root) => {
232
+ if (conf.positionals.length) {
233
+ throw error('`vlt setup --path` takes no arguments', {
234
+ code: 'EUSAGE',
235
+ found: conf.positionals[0],
236
+ });
237
+ }
238
+ return {
239
+ path: await runPathSetup({
240
+ globalDir: root,
241
+ globalBinDir: conf.globalBinDir,
242
+ yes: !!conf.get('yes'),
243
+ noModifyPath: !!conf.get('no-modify-path'),
244
+ question: ({ dir, rcs }) => `Add ${dir} to your PATH in ${rcs}? (Y/n) `,
245
+ }),
246
+ };
247
+ };
248
+ // end of the wizard, in a terminal only
249
+ const offerPath = async (conf, yes, ask) => {
250
+ if (yes || !process.stdin.isTTY || conf.get('no-modify-path')) {
251
+ return;
252
+ }
253
+ const g = conf.globalProject();
254
+ if (!g)
255
+ return;
256
+ try {
257
+ return await runPathSetup({
258
+ globalDir: g.root,
259
+ globalBinDir: g.binDir,
260
+ question: () => 'Setup PATH for global installs? (Y/n) ',
261
+ ask,
262
+ });
263
+ }
264
+ catch (er) {
265
+ // registries are already saved
266
+ const e = asError(er);
267
+ // e.g. the global-bin-dir that could not be created
268
+ const found = (isErrorWithCause(e) &&
269
+ isObject(e.cause) &&
270
+ typeof e.cause.found === 'string') ?
271
+ prettyPath(e.cause.found)
272
+ : undefined;
273
+ return {
274
+ status: 'failed',
275
+ message: found && !e.message.includes(found) ?
276
+ `${e.message}: ${found}`
277
+ : e.message,
278
+ };
279
+ }
280
+ };
106
281
  export const command = async (conf) => {
282
+ // set only by --path, see Config#globalProject
283
+ const root = conf.globalRoot;
284
+ if (root)
285
+ return setupPath(conf, root);
107
286
  const yes = !!conf.get('yes');
108
287
  const which = configWriteTarget(conf, 'user');
109
288
  let rl;
@@ -198,11 +377,14 @@ export const command = async (conf) => {
198
377
  // worked.
199
378
  const shadowedByProject = which === 'user' &&
200
379
  registrySelectionFields.some(f => f in (conf.layers.project ?? {}));
380
+ // 8. offer PATH setup for global installs
381
+ const path = await offerPath(conf, yes, ask);
201
382
  return {
202
383
  account,
203
384
  which,
204
385
  registries,
205
386
  ...(shadowedByProject ? { shadowedByProject } : undefined),
387
+ ...(path ? { path } : undefined),
206
388
  };
207
389
  }
208
390
  finally {
@@ -21,9 +21,8 @@ export const usage = () => commandUsage({
21
21
  \`.agents/skills\`, plus \`<folder>/skills\` for each agent folder
22
22
  at the project root (e.g. \`.claude/\`), or the \`skills-dir\`
23
23
  config. When packages ship skills with the same name, one is
24
- linked and the others are reported. Only links made by vlt
25
- (pointing into \`node_modules/.vlt\`) are ever replaced or
26
- removed; anything else in the way is skipped.
24
+ linked and the others are reported. Only links made by vlt are
25
+ ever replaced or removed; anything else in the way is skipped.
27
26
 
28
27
  Use --target or a query after the subcommand to select packages
29
28
  using DSS query syntax. End a selector with
@@ -105,7 +105,7 @@ export const views = {
105
105
  },
106
106
  };
107
107
  const listTokens = async (rc, registry, identity, alias) => {
108
- const localTok = await getToken(normalizeRegistryKey(registry), identity);
108
+ const localTok = await getToken(normalizeRegistryKey(registry), identity, rc.defaultRegistryKey);
109
109
  if (!localTok) {
110
110
  return { registry, alias, tokens: [] };
111
111
  }
@@ -1,5 +1,6 @@
1
1
  import type { Graph, LinkSkillsResult } from '@vltpkg/graph';
2
2
  import type { CommandFn, CommandUsage } from '../index.ts';
3
+ import type { SelfRefused } from '../self-update.ts';
3
4
  export type UninstallResult = {
4
5
  /**
5
6
  * The resulting graph structure at the end of an uninstall.
@@ -10,11 +11,17 @@ export type UninstallResult = {
10
11
  */
11
12
  skills?: LinkSkillsResult;
12
13
  };
14
+ /** `vlt uninstall -g vlt` of a vlt that is not a global package */
15
+ export type UninstallCommandResult = UninstallResult | {
16
+ self: SelfRefused;
17
+ };
13
18
  export declare const needsRegistry = true;
14
19
  export declare const needsNpmRegistry = true;
15
20
  export declare const usage: CommandUsage;
16
21
  export declare const views: {
17
- readonly json: (i: UninstallResult) => import("@vltpkg/graph").LockfileData;
18
- readonly human: import("../view.ts").LazyView<import("./install.ts").InstallResult>;
22
+ readonly json: (i: UninstallCommandResult) => import("@vltpkg/graph").LockfileData | {
23
+ self: SelfRefused;
24
+ };
25
+ readonly human: import("../view.ts").LazyView<Partial<import("./install.ts").InstallResult>>;
19
26
  };
20
- export declare const command: CommandFn<UninstallResult>;
27
+ export declare const command: CommandFn<UninstallCommandResult>;
@@ -1,6 +1,9 @@
1
1
  import { uninstall } from '@vltpkg/graph';
2
2
  import { commandUsage } from "../config/usage.js";
3
3
  import { parseRemoveArgs } from "../parse-add-remove-args.js";
4
+ import { assertGlobalOptions, parseGlobalRemoveArgs, removeGlobalPackages, } from "../global.js";
5
+ import { resolveGlobalBinDir } from "../global-bin-dir.js";
6
+ import { deferSelfRemove, refuseSelfUninstall, runningStoreDep, } from "../self-update.js";
4
7
  import { lazyView } from "../view.js";
5
8
  export const needsRegistry = true;
6
9
  export const needsNpmRegistry = true;
@@ -25,26 +28,49 @@ export const usage = () => commandUsage({
25
28
  value: '<query>',
26
29
  description: 'Filter which packages are allowed to run lifecycle scripts using DSS query syntax.',
27
30
  },
31
+ global: {
32
+ description: 'Uninstall global packages, removing their workspace and bins.',
33
+ },
28
34
  },
29
35
  });
30
36
  export const views = {
31
- json: i => i.graph.toJSON(),
37
+ json: i => ('graph' in i ? i.graph.toJSON() : { self: i.self }),
32
38
  human: lazyView(async () => (await import('./install/reporter.ts')).InstallReporter),
33
39
  };
34
40
  export const command = async (conf) => {
41
+ if (conf.globalRoot) {
42
+ assertGlobalOptions(conf);
43
+ const refused = await refuseSelfUninstall(conf);
44
+ if (refused)
45
+ return { self: refused };
46
+ }
35
47
  const { monorepo, scurry } = conf.options;
36
- const { remove } = parseRemoveArgs(conf, scurry, monorepo);
48
+ const global = conf.globalRoot ? parseGlobalRemoveArgs(conf) : undefined;
49
+ // only swept, so a bad --global-bin-dir must not block it
50
+ const binDir = global ?
51
+ await resolveGlobalBinDir(conf).catch(() => undefined)
52
+ : undefined;
53
+ const { remove } = global ?? parseRemoveArgs(conf, scurry, monorepo);
37
54
  /* c8 ignore start */
38
55
  const allowScripts = conf.get('allow-scripts') ?
39
56
  String(conf.get('allow-scripts'))
40
57
  : ':not(*)';
41
58
  /* c8 ignore stop */
59
+ // never removed from under itself
60
+ const keep = conf.globalRoot ?
61
+ await runningStoreDep(conf.globalRoot)
62
+ : undefined;
42
63
  const { graph, skills } = await uninstall({
43
64
  ...conf.options,
44
65
  allowScripts,
45
66
  allowSkills: conf.get('allow-skills'),
46
67
  skillsDirs: conf.get('skills-dir'),
47
68
  lockfileOnly: conf.options['lockfile-only'],
69
+ ...(keep ? { keepNodes: new Set([keep]) } : null),
48
70
  }, remove);
71
+ if (global) {
72
+ await deferSelfRemove(conf.projectRoot, graph, keep);
73
+ await removeGlobalPackages(conf.projectRoot, global.dirs, binDir?.dir ?? conf.globalBinDir);
74
+ }
49
75
  return { graph, ...(skills ? { skills } : null) };
50
76
  };
@@ -22,6 +22,6 @@ export declare const views: {
22
22
  buildQueue?: import("@vltpkg/dep-id").DepID[] | undefined;
23
23
  message?: string | undefined;
24
24
  };
25
- readonly human: import("../view.ts").LazyView<InstallResult>;
25
+ readonly human: import("../view.ts").LazyView<Partial<InstallResult>>;
26
26
  };
27
27
  export declare const command: CommandFn<InstallResult>;
@@ -236,6 +236,38 @@ export declare const definition: import("jackspeak").Jack<{
236
236
  } & {
237
237
  workspace: import("jackspeak").ConfigOption<"string", true, readonly string[] | undefined>;
238
238
  'workspace-group': import("jackspeak").ConfigOption<"string", true, readonly string[] | undefined>;
239
+ } & {
240
+ global: {
241
+ short: string;
242
+ description: string;
243
+ };
244
+ } & {
245
+ global: import("jackspeak").ConfigOption<"boolean", false, undefined>;
246
+ } & {
247
+ 'global-dir': {
248
+ hint: string;
249
+ default: string;
250
+ description: string;
251
+ };
252
+ } & {
253
+ 'global-dir': import("jackspeak").ConfigOption<"string", false, readonly string[] | undefined>;
254
+ } & {
255
+ 'global-bin-dir': {
256
+ hint: string;
257
+ description: string;
258
+ };
259
+ } & {
260
+ 'global-bin-dir': import("jackspeak").ConfigOption<"string", false, readonly string[] | undefined>;
261
+ } & {
262
+ path: {
263
+ description: string;
264
+ };
265
+ 'no-modify-path': {
266
+ description: string;
267
+ };
268
+ } & {
269
+ path: import("jackspeak").ConfigOption<"boolean", false, undefined>;
270
+ 'no-modify-path': import("jackspeak").ConfigOption<"boolean", false, undefined>;
239
271
  } & {
240
272
  scope: {
241
273
  short: string;
@@ -474,6 +474,66 @@ export const definition = j
474
474
  description: `Specify named workspace group names to load and operate on
475
475
  when doing recursive operations on workspaces.`,
476
476
  },
477
+ })
478
+ .flag({
479
+ global: {
480
+ short: 'g',
481
+ description: `Operate on the global project instead of the current
482
+ one. Supported by \`install\`, \`uninstall\`, \`list\`,
483
+ \`query\` and \`build\`.
484
+
485
+ Each global package is installed in its own workspace of
486
+ the project in \`--global-dir\`, and its bins are linked
487
+ into \`--global-bin-dir\`.`,
488
+ },
489
+ })
490
+ .opt({
491
+ 'global-dir': {
492
+ hint: 'path',
493
+ default: xdg.data('global'),
494
+ description: `Location of the global project used by \`--global\`.
495
+ Defaults to \`vlt/global\` in the XDG data directory.
496
+
497
+ Only read from the command line, environment or the user
498
+ config, never from a project \`vlt.json\`. A leading
499
+ \`~\` is the home directory. Must be absolute in the
500
+ user config.`,
501
+ },
502
+ })
503
+ .opt({
504
+ 'global-bin-dir': {
505
+ hint: 'path',
506
+ description: `Folder where bins of global packages are linked.
507
+
508
+ Defaults to the folder of the \`vlt\` found in your
509
+ \`PATH\` when it is writable, so bins run right away.
510
+ If it is not writable, \`~/.local/bin\` is used when
511
+ it is in your \`PATH\` (not on Windows). Otherwise
512
+ \`<global-dir>/bin\`.
513
+
514
+ Only read from the command line, environment or the
515
+ user config, never from a project \`vlt.json\`. A
516
+ leading \`~\` is the home directory. Must be absolute
517
+ in the user config.`,
518
+ },
519
+ })
520
+ .flag({
521
+ path: {
522
+ description: `With \`vlt setup\`, add the global bin folder
523
+ (\`--global-bin-dir\`) to your \`PATH\` by editing
524
+ your shell config (your user \`Path\` on Windows).
525
+ Asks first, unless \`--yes\`. Command line only.`,
526
+ },
527
+ 'no-modify-path': {
528
+ description: `Never edit shell config files (or the user
529
+ \`Path\` on Windows) to add a folder to your
530
+ \`PATH\`: turns off \`vlt setup --path\` edits
531
+ and the \`vlt install -g\` offer (printing the line
532
+ to add instead), and the \`vlt setup\` PATH prompt.
533
+ Set \`VLT_NO_MODIFY_PATH=1\` (any value but \`0\`)
534
+ in images or on machines whose dotfiles are
535
+ managed elsewhere.`,
536
+ },
477
537
  })
478
538
  .opt({
479
539
  scope: {
@@ -77,7 +77,7 @@ export type ConfigFileData = ConfigFileDataNoCommand & {
77
77
  export type ConfigOptionsNoExtras = {
78
78
  [k in keyof OptionsResults<ConfigDefinitions>]: k extends (RecordField) ? RecordString : k extends 'command' ? never : OptionsResults<ConfigDefinitions>[k];
79
79
  };
80
- export type ConfigOptions = ConfigOptionsNoExtras & Pick<SpecOptions, 'catalog' | 'catalogs'> & {
80
+ export type ConfigOptions = Omit<ConfigOptionsNoExtras, 'path'> & Pick<SpecOptions, 'catalog' | 'catalogs'> & {
81
81
  packageJson: PackageJson;
82
82
  scurry: PathScurry;
83
83
  projectRoot: string;
@@ -142,6 +142,24 @@ export declare class Config {
142
142
  * `~/projects/xyz`, then the highest dir it will check is `~/projects`
143
143
  */
144
144
  projectRoot: string;
145
+ /**
146
+ * Global project root, set when `--global` switched to it.
147
+ */
148
+ globalRoot?: string;
149
+ /**
150
+ * `--global-bin-dir` from cli, env or user config, set with
151
+ * `globalRoot`.
152
+ */
153
+ globalBinDir?: string;
154
+ /**
155
+ * The global project (`--global-dir`, `--global-bin-dir`: cli > env >
156
+ * user config, never a project `vlt.json`): the one switched to by
157
+ * `--global`, or the one `vlt setup` offers PATH setup for.
158
+ */
159
+ globalProject(): {
160
+ root: string;
161
+ binDir?: string;
162
+ } | undefined;
145
163
  /**
146
164
  * `Record<alias, canonical name>` to dereference command aliases.
147
165
  */
@@ -221,8 +239,11 @@ export declare class Config {
221
239
  /**
222
240
  * Find the local config file and load both it and the user-level config in
223
241
  * the XDG config home.
242
+ *
243
+ * When `args` select a `--global` command, the global project is used
244
+ * instead of the current one.
224
245
  */
225
- loadConfigFile(): Promise<this>;
246
+ loadConfigFile(args?: string[]): Promise<this>;
226
247
  /**
227
248
  * Force a complete reload of config files from disk.
228
249
  * This clears all caches and re-reads config files.
@@ -238,7 +259,9 @@ export declare class Config {
238
259
  * only used in tests, resets the memoization
239
260
  * @internal
240
261
  */
241
- reload?: boolean): Promise<LoadedConfig>;
262
+ reload?: boolean,
263
+ /** switch to the global project on `--global` */
264
+ allowGlobal?: boolean): Promise<LoadedConfig>;
242
265
  }
243
266
  export type ParsedConfig = Config & {
244
267
  command: NonNullable<Config['command']>;