@qui-cli/core 6.0.2 → 6.0.3

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/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
4
4
 
5
+ ## [6.0.3](https://github.com/battis/qui-cli/compare/core/6.0.2...core/6.0.3) (2026-02-22)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * allow overlaying configurations in Jackspeak, as in other plugins ([29bc7ad](https://github.com/battis/qui-cli/commit/29bc7adfdbafb3d961e0768988473d3f2ecd6aaf))
11
+ * extirpate last non-debugging console.log invocation ([d67975d](https://github.com/battis/qui-cli/commit/d67975d80aa47b2e5ffefdb2fff35fbdec726277))
12
+ * initialize Positionals options to correctly display plugin-required positionals ([fde874c](https://github.com/battis/qui-cli/commit/fde874ca39d9823892d49ff3009a9c2afece84eb))
13
+
5
14
  ## [6.0.2](https://github.com/battis/qui-cli/compare/core/6.0.1...core/6.0.2) (2026-02-21)
6
15
 
7
16
 
package/dist/Core.d.ts CHANGED
@@ -2,6 +2,7 @@ import * as Plugin from '@qui-cli/plugin';
2
2
  import * as JackSpeak from './JackSpeak.js';
3
3
  export { Options } from '@qui-cli/plugin';
4
4
  export * from './Usage.js';
5
+ /** Core configuration options */
5
6
  export type Configuration = Plugin.Registrar.Configuration & {
6
7
  /**
7
8
  * Usage information for plugins is diplayed in LIFO (last-in, first-out)
@@ -18,6 +19,34 @@ export type Configuration = Plugin.Registrar.Configuration & {
18
19
  requirePositionals?: boolean | number;
19
20
  };
20
21
  };
21
- export declare function configure(config?: Configuration): Promise<void>;
22
+ /**
23
+ * Configure core options
24
+ *
25
+ * May be called multiple times, overlaying partial configurations they become
26
+ * available/defined
27
+ *
28
+ * @see {@link Configuration}
29
+ */
30
+ export declare function configure(proposal?: Configuration): Promise<void>;
31
+ /**
32
+ * Initialize plugins from the environment and provided command line options
33
+ *
34
+ * May only be called once
35
+ *
36
+ * @param externalOptions Optional additional options to initialze
37
+ * @returns Result of
38
+ * {@link https://github.com/isaacs/jackspeak?tab=readme-ov-file#jackparseargs-string--processargv--positionals-string-values-optionsresults- Jackspeak.parse()}
39
+ * (Additional Jackspeak configuration may be done via the {@link JackSpeak}
40
+ * core plugin)
41
+ * @throws If invoked after {@link run} invocation or otherwise invoked for a
42
+ * second time
43
+ */
22
44
  export declare function init(externalOptions?: Plugin.Options): Promise<Plugin.Arguments<Plugin.Options>>;
45
+ /**
46
+ * Initialize plugins from provided command line options and the environment, if
47
+ * not already initialized and run registered plugins.
48
+ *
49
+ * @param externalOptions Optional additional options to initialze
50
+ * @returns Hash of accumulated results of all plugin `run()` methods
51
+ */
23
52
  export declare function run(externalOptions?: Plugin.Options): Promise<Plugin.AccumulatedResults | undefined>;
package/dist/Core.js CHANGED
@@ -4,10 +4,21 @@ import * as JackSpeak from './JackSpeak.js';
4
4
  import * as Positionals from './Positionals.js';
5
5
  export * from './Usage.js';
6
6
  let initialized = false;
7
- let lifoUsage = true;
8
- export async function configure(config = {}) {
9
- lifoUsage = Plugin.hydrate(config.lifoUsage, lifoUsage);
10
- const { core = {}, jackspeak: jackOptions, positionals = {} } = config;
7
+ // TODO improve typing
8
+ // Issue URL: https://github.com/battis/qui-cli/issues/100
9
+ // @ts-expect-error 2322
10
+ const config = { lifoUsage: true };
11
+ /**
12
+ * Configure core options
13
+ *
14
+ * May be called multiple times, overlaying partial configurations they become
15
+ * available/defined
16
+ *
17
+ * @see {@link Configuration}
18
+ */
19
+ export async function configure(proposal = {}) {
20
+ config.lifoUsage = Plugin.hydrate(proposal.lifoUsage, config.lifoUsage);
21
+ const { core = {}, jackspeak: jackOptions, positionals = {} } = proposal;
11
22
  const { requirePositionals, ...deprecated } = core;
12
23
  const jackspeak = {
13
24
  ...deprecated,
@@ -24,12 +35,23 @@ function requireUnusedOptions(plugin) {
24
35
  plugin.name !== Positionals.name &&
25
36
  plugin.name !== Help.name);
26
37
  }
38
+ /**
39
+ * Initialize plugins from the environment and provided command line options
40
+ *
41
+ * May only be called once
42
+ *
43
+ * @param externalOptions Optional additional options to initialze
44
+ * @returns Result of
45
+ * {@link https://github.com/isaacs/jackspeak?tab=readme-ov-file#jackparseargs-string--processargv--positionals-string-values-optionsresults- Jackspeak.parse()}
46
+ * (Additional Jackspeak configuration may be done via the {@link JackSpeak}
47
+ * core plugin)
48
+ * @throws If invoked after {@link run} invocation or otherwise invoked for a
49
+ * second time
50
+ */
27
51
  export async function init(externalOptions) {
28
52
  if (initialized) {
29
53
  throw new Error(`Already initialized with user-provided command line arguments.`);
30
54
  }
31
- JackSpeak.args(Help.options());
32
- JackSpeak.args(Positionals.options());
33
55
  const usage = [
34
56
  ...(await Promise.all(Plugin.Registrar.registered()
35
57
  .filter(requireUnusedOptions)
@@ -38,9 +60,11 @@ export async function init(externalOptions) {
38
60
  if (externalOptions) {
39
61
  usage.push(Plugin.documentDefaults(externalOptions));
40
62
  }
41
- if (lifoUsage) {
63
+ if (config.lifoUsage) {
42
64
  usage.reverse();
43
65
  }
66
+ JackSpeak.args(Help.options());
67
+ JackSpeak.args(Positionals.options());
44
68
  for (const options of usage) {
45
69
  JackSpeak.args(options);
46
70
  }
@@ -49,6 +73,13 @@ export async function init(externalOptions) {
49
73
  initialized = true;
50
74
  return args;
51
75
  }
76
+ /**
77
+ * Initialize plugins from provided command line options and the environment, if
78
+ * not already initialized and run registered plugins.
79
+ *
80
+ * @param externalOptions Optional additional options to initialze
81
+ * @returns Hash of accumulated results of all plugin `run()` methods
82
+ */
52
83
  export async function run(externalOptions) {
53
84
  if (!initialized) {
54
85
  await init(externalOptions);
package/dist/Help.js CHANGED
@@ -18,7 +18,7 @@ export function options() {
18
18
  export function init({ values }) {
19
19
  configure(values);
20
20
  if (help) {
21
- console.log(Core.usage());
21
+ process.stdout.write(Core.usage());
22
22
  process.exit(0);
23
23
  }
24
24
  }
@@ -2,10 +2,26 @@ import * as Plugin from '@qui-cli/plugin';
2
2
  import { Jack, JackOptions } from 'jackspeak';
3
3
  export type Configuration = Plugin.Configuration & JackOptions;
4
4
  export declare const name = "jackspeak";
5
+ /** Jackspeak instance used to parse command line arguments */
5
6
  export declare function jack(): Jack<{}>;
6
- export declare function configure(config?: Configuration): void;
7
+ /**
8
+ * Configure Jackspeak options
9
+ *
10
+ * @see {@link JackOptions}
11
+ */
12
+ export declare function configure(proposal?: Configuration): void;
13
+ /**
14
+ * Apply {@link Plugin.Options} to Jackspeak
15
+ *
16
+ * Generally called only by Core
17
+ */
7
18
  export declare function args(options: Plugin.Options): void;
19
+ /**
20
+ * Convenience pass-through to
21
+ * {@link https://github.com/isaacs/jackspeak?tab=readme-ov-file#jackparseargs-string--processargv--positionals-string-values-optionsresults- Jackspeak.parse()}
22
+ */
8
23
  export declare function parse(): import("jackspeak").Parsed<{}>;
24
+ /** Convenience pass-through to (undocumented) Jackspeak.toJSON() */
9
25
  export declare function toJSON(): {
10
26
  [k: string]: {
11
27
  hint?: string | undefined;
@@ -19,5 +35,13 @@ export declare function toJSON(): {
19
35
  type: import("jackspeak").ConfigType;
20
36
  };
21
37
  };
38
+ /**
39
+ * Convenience pass-through to
40
+ * {@link https://github.com/isaacs/jackspeak?tab=readme-ov-file#jackusage-string Jackspeak.usage()}
41
+ */
22
42
  export declare function usage(): string;
43
+ /**
44
+ * Convenience pass-through to
45
+ * {@link https://github.com/isaacs/jackspeak?tab=readme-ov-file#jackusagemarkdown-string Jackspeak.usageMarkdown()}
46
+ */
23
47
  export declare function usageMarkdown(): string;
package/dist/JackSpeak.js CHANGED
@@ -1,18 +1,39 @@
1
1
  import { Jack } from 'jackspeak';
2
2
  export const name = 'jackspeak';
3
+ const config = {};
3
4
  let instance = undefined;
5
+ /** Jackspeak instance used to parse command line arguments */
4
6
  export function jack() {
5
7
  if (!instance) {
6
- configure();
8
+ instance = new Jack(config);
7
9
  }
8
10
  if (!instance) {
9
11
  throw new Error(`JackSpeak configuration failed.`);
10
12
  }
11
13
  return instance;
12
14
  }
13
- export function configure(config = {}) {
14
- instance = new Jack(config);
15
+ /**
16
+ * Configure Jackspeak options
17
+ *
18
+ * @see {@link JackOptions}
19
+ */
20
+ export function configure(proposal = {}) {
21
+ let changed = false;
22
+ for (const key in proposal) {
23
+ if (proposal[key] !== undefined) {
24
+ changed = config[key] !== proposal[key];
25
+ config[key] = proposal[key];
26
+ }
27
+ }
28
+ if (changed) {
29
+ instance = undefined;
30
+ }
15
31
  }
32
+ /**
33
+ * Apply {@link Plugin.Options} to Jackspeak
34
+ *
35
+ * Generally called only by Core
36
+ */
16
37
  export function args(options) {
17
38
  for (const key in options) {
18
39
  if (key === 'man') {
@@ -36,15 +57,28 @@ export function args(options) {
36
57
  }
37
58
  }
38
59
  }
60
+ /**
61
+ * Convenience pass-through to
62
+ * {@link https://github.com/isaacs/jackspeak?tab=readme-ov-file#jackparseargs-string--processargv--positionals-string-values-optionsresults- Jackspeak.parse()}
63
+ */
39
64
  export function parse() {
40
65
  return jack().parse();
41
66
  }
67
+ /** Convenience pass-through to (undocumented) Jackspeak.toJSON() */
42
68
  export function toJSON() {
43
69
  return jack().toJSON();
44
70
  }
71
+ /**
72
+ * Convenience pass-through to
73
+ * {@link https://github.com/isaacs/jackspeak?tab=readme-ov-file#jackusage-string Jackspeak.usage()}
74
+ */
45
75
  export function usage() {
46
76
  return jack().usage();
47
77
  }
78
+ /**
79
+ * Convenience pass-through to
80
+ * {@link https://github.com/isaacs/jackspeak?tab=readme-ov-file#jackusagemarkdown-string Jackspeak.usageMarkdown()}
81
+ */
48
82
  export function usageMarkdown() {
49
83
  return jack().usageMarkdown();
50
84
  }
@@ -1,6 +1,11 @@
1
1
  import * as Plugin from '@qui-cli/plugin';
2
2
  type PositionalConfig = {
3
- /** Description of the purpose of the positional argument */
3
+ /**
4
+ * Optional description of the purpose of the positional argument.
5
+ *
6
+ * If not provided, the argument will not be separately listed in the
7
+ * Positional usage section
8
+ */
4
9
  description?: string;
5
10
  /** Hint about the value of the positional argument */
6
11
  hint?: string;
@@ -9,6 +14,13 @@ type PositionalConfig = {
9
14
  };
10
15
  type PositionalConfigSet = Record<string, PositionalConfig>;
11
16
  export type Configuration = Plugin.Configuration & {
17
+ /**
18
+ * Preamble description paragraph(s) to Positionals usage section
19
+ *
20
+ * This may provide a clearer way of explaining positional arguments than
21
+ * listing them like options.
22
+ */
23
+ description?: string | string[];
12
24
  /** Minimum number of accepted positional arguments */
13
25
  min?: number;
14
26
  /** Maximum number of accepted positional arguments */
@@ -17,7 +29,7 @@ export type Configuration = Plugin.Configuration & {
17
29
  export declare const name = "positionals";
18
30
  /** @deprecated Do not use, included for backwards compatibility */
19
31
  export declare function requirePositionalsIsDeprecatedAndShouldNotBeUsed(positionals: Configuration, arg?: boolean | number): number;
20
- export declare function configure(config?: Configuration): void;
32
+ export declare function configure(proposal?: Configuration): void;
21
33
  export declare function require(positionalConfigSet: PositionalConfigSet): void;
22
34
  export declare function minimumArgCount(): number;
23
35
  export declare function requireAtLeast(minimumArgs: number): void;
@@ -1,9 +1,7 @@
1
- import * as Colors from '@qui-cli/colors/dist/Colors.js';
2
- import * as Plugin from '@qui-cli/plugin';
1
+ import { Colors } from '@qui-cli/colors';
3
2
  import wrapAnsi from 'wrap-ansi';
4
3
  export const name = 'positionals';
5
- let min = 0;
6
- let max = undefined;
4
+ const config = { min: 0 };
7
5
  const configSet = {};
8
6
  let positionals = [];
9
7
  /** @deprecated Do not use, included for backwards compatibility */
@@ -19,13 +17,17 @@ export function requirePositionalsIsDeprecatedAndShouldNotBeUsed(positionals, ar
19
17
  requireAtLeast(1);
20
18
  }
21
19
  }
22
- return min;
20
+ return config.min;
23
21
  }
24
- export function configure(config = {}) {
25
- requireAtLeast(Plugin.hydrate(config.min, min));
26
- const m = Plugin.hydrate(config.max, max);
27
- if (m !== undefined) {
28
- requireNoMoreThan(m);
22
+ export function configure(proposal = {}) {
23
+ for (const key in proposal) {
24
+ if (proposal[key] !== undefined) {
25
+ config[key] = proposal[key];
26
+ }
27
+ }
28
+ requireAtLeast(config.min);
29
+ if (config.max !== undefined) {
30
+ requireNoMoreThan(config.max);
29
31
  }
30
32
  }
31
33
  export function require(positionalConfigSet) {
@@ -36,9 +38,9 @@ export function require(positionalConfigSet) {
36
38
  }
37
39
  configSet[name] = positionalConfigSet[name];
38
40
  }
39
- min += names.length;
40
- if (max !== undefined) {
41
- max += names.length;
41
+ config.min += names.length;
42
+ if (config.max !== undefined) {
43
+ config.max += names.length;
42
44
  }
43
45
  }
44
46
  function names() {
@@ -48,36 +50,36 @@ function namedCount() {
48
50
  return names().length;
49
51
  }
50
52
  export function minimumArgCount() {
51
- return min;
53
+ return config.min;
52
54
  }
53
55
  export function requireAtLeast(minimumArgs) {
54
56
  if (minimumArgs < 0) {
55
57
  throw new Error(`Cannot require fewer than 0 positional args.`);
56
58
  }
57
- if (max !== undefined && minimumArgs > max) {
58
- throw new Error(`Cannot require min ${minimumArgs} positional args, maximum ${max} required positional args are configured.`);
59
+ if (config.max !== undefined && minimumArgs > config.max) {
60
+ throw new Error(`Cannot require min ${minimumArgs} positional args, maximum ${config.max} required positional args are configured.`);
59
61
  }
60
- min = minimumArgs;
62
+ config.min = minimumArgs;
61
63
  }
62
64
  export function maximumArgCount() {
63
- return max;
65
+ return config.max;
64
66
  }
65
67
  export function requireNoMoreThan(maximumArgs) {
66
- if (maximumArgs >= min && maximumArgs >= namedCount()) {
67
- max = maximumArgs;
68
+ if (maximumArgs >= (config.min || 0) && maximumArgs >= namedCount()) {
69
+ config.max = maximumArgs;
68
70
  }
69
71
  else {
70
- throw new Error(`Cannot require max ${max} positional args, minimum ${namedCount()} required positional args are configured.`);
72
+ throw new Error(`Cannot require max ${config.max} positional args, minimum ${namedCount()} required positional args are configured.`);
71
73
  }
72
74
  }
73
75
  export function allowOptionalArgs() {
74
- max = undefined;
76
+ config.max = undefined;
75
77
  }
76
78
  export function allowOnlyNamedArgs() {
77
- requireNoMoreThan(min);
79
+ requireNoMoreThan(config.min);
78
80
  }
79
81
  export function minimumUnnamedArgCount() {
80
- return Math.max(0, min - namedCount());
82
+ return Math.max(0, config.min - namedCount());
81
83
  }
82
84
  /**
83
85
  * Caution: this should not be set within plugins due to potential confusion and
@@ -86,13 +88,13 @@ export function minimumUnnamedArgCount() {
86
88
  * @param minUnnamed Number of optional args to allow
87
89
  */
88
90
  export function requireAtLeastUnnamedArgs(minUnnamed) {
89
- if (max && min + minUnnamed > max) {
90
- throw new Error(`Cannot require min ${minUnnamed} unnamed positional args, maximum ${max - namedCount()} unnamed positioal args are configure.`);
91
+ if (config.max && config.min + minUnnamed > config.max) {
92
+ throw new Error(`Cannot require min ${minUnnamed} unnamed positional args, maximum ${config.max - namedCount()} unnamed positioal args are configure.`);
91
93
  }
92
- requireAtLeast(min + minUnnamed);
94
+ requireAtLeast(config.min + minUnnamed);
93
95
  }
94
96
  export function maximumUnnamedCount() {
95
- return max ? max - namedCount() : undefined;
97
+ return config.max ? config.max - namedCount() : undefined;
96
98
  }
97
99
  /**
98
100
  * Caution: this should not be set within plugins due to potential confusion and
@@ -104,21 +106,33 @@ export function requireNoMoreThanUnnamedArgs(maxUnnamed) {
104
106
  if (maxUnnamed < 0) {
105
107
  throw new Error(`Cannot require a negative number of unnamed positional args.`);
106
108
  }
107
- if (maxUnnamed < min - namedCount()) {
108
- throw new Error(`Cannot require max ${maxUnnamed} unnamed positional args, minimum ${min - namedCount()} unnamed positional args are configured.`);
109
+ if (maxUnnamed < config.min - namedCount()) {
110
+ throw new Error(`Cannot require max ${maxUnnamed} unnamed positional args, minimum ${config.min - namedCount()} unnamed positional args are configured.`);
109
111
  }
110
112
  requireNoMoreThan(namedCount() + maxUnnamed);
111
113
  }
112
114
  export function options() {
113
115
  const man = [];
116
+ if (config.description) {
117
+ if (Array.isArray(config.description)) {
118
+ man.push(...config.description.map((text) => ({ text })));
119
+ }
120
+ else {
121
+ man.push({ text: config.description });
122
+ }
123
+ }
114
124
  for (const arg in configSet) {
115
- man.push({
116
- level: 2,
117
- text: `${Colors.positionalArg(arg)}${configSet[arg].hint ? `=<${configSet[arg].hint}>` : ''}`
118
- });
125
+ if (configSet[arg].description || configSet[arg].hint)
126
+ man.push({
127
+ level: 2,
128
+ text: Colors.positionalArg(arg)
129
+ });
119
130
  if (configSet[arg].description) {
120
131
  man.push({ text: configSet[arg].description });
121
132
  }
133
+ if (configSet[arg].hint) {
134
+ man.push({ text: configSet[arg].hint });
135
+ }
122
136
  }
123
137
  if (man.length > 0) {
124
138
  man.unshift({ level: 1, text: 'Positional arguments' });
@@ -149,15 +163,15 @@ function ellipsis(args, start, end) {
149
163
  }
150
164
  export function usageArgs() {
151
165
  let args = names();
152
- args = unnamed(args, (max || min) - namedCount());
153
- if (!max) {
166
+ args = unnamed(args, (config.max || config.min) - namedCount());
167
+ if (!config.max) {
154
168
  args.push('...');
155
169
  }
156
- args = optional(args, min);
157
- if (max) {
158
- args = ellipsis(args, min, args.length);
170
+ args = optional(args, config.min);
171
+ if (config.max) {
172
+ args = ellipsis(args, config.min, args.length);
159
173
  }
160
- args = ellipsis(args, namedCount(), min);
174
+ args = ellipsis(args, namedCount(), config.min);
161
175
  return args.map((arg) => Colors.positionalArg(arg)).join(' ');
162
176
  }
163
177
  function wrap(text, indent) {
@@ -187,11 +201,11 @@ export function usage(usage, isMarkdown = false) {
187
201
  }
188
202
  export function run() {
189
203
  const s = positionals.length !== 1 ? 's' : '';
190
- if (positionals.length < min) {
191
- throw new Error(`Expected at least ${min} positional arguments, received ${positionals.length} positional argument${s}.`);
204
+ if (positionals.length < config.min) {
205
+ throw new Error(`Expected at least ${config.min} positional arguments, received ${positionals.length} positional argument${s}.`);
192
206
  }
193
- if (max !== undefined && positionals.length > max) {
194
- throw new Error(`Expected no more than ${max} positional arguments, received ${positionals.length} positional argument${s}.`);
207
+ if (config.max !== undefined && positionals.length > config.max) {
208
+ throw new Error(`Expected no more than ${config.max} positional arguments, received ${positionals.length} positional argument${s}.`);
195
209
  }
196
210
  names().forEach((name, i) => {
197
211
  if (configSet[name].validate) {
package/dist/index.js CHANGED
@@ -2,8 +2,8 @@ import { register } from '@qui-cli/plugin';
2
2
  import * as Help from './Help.js';
3
3
  import * as JackSpeak from './JackSpeak.js';
4
4
  import * as Positionals from './Positionals.js';
5
+ export * as Core from './Core.js';
6
+ export { Help, JackSpeak, Positionals };
5
7
  await register(Help);
6
8
  await register(JackSpeak);
7
9
  await register(Positionals);
8
- export * as Core from './Core.js';
9
- export { Help, JackSpeak, Positionals };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qui-cli/core",
3
- "version": "6.0.2",
3
+ "version": "6.0.3",
4
4
  "description": "Core features of @qui-cli/qui-cli",
5
5
  "homepage": "https://github.com/battis/qui-cli/tree/main/packages/core#readme",
6
6
  "repository": {