burgee 0.0.0 → 0.2.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.
Files changed (114) hide show
  1. package/README.md +28 -1
  2. package/dist/agent.d.ts +19 -0
  3. package/dist/agent.js +25 -0
  4. package/dist/brand.d.ts +186 -0
  5. package/dist/brand.js +232 -0
  6. package/dist/cli.d.ts +51 -0
  7. package/dist/cli.js +105 -0
  8. package/dist/commander-argument.d.ts +22 -0
  9. package/dist/commander-argument.js +72 -0
  10. package/dist/commander-command.d.ts +345 -0
  11. package/dist/commander-command.js +1605 -0
  12. package/dist/commander-error.d.ts +10 -0
  13. package/dist/commander-error.js +20 -0
  14. package/dist/commander-help.d.ts +67 -0
  15. package/dist/commander-help.js +319 -0
  16. package/dist/commander-option.d.ts +58 -0
  17. package/dist/commander-option.js +164 -0
  18. package/dist/commander-suggest.d.ts +2 -0
  19. package/dist/commander-suggest.js +59 -0
  20. package/dist/commander.d.ts +18 -0
  21. package/dist/commander.js +12 -0
  22. package/dist/completions.d.ts +39 -0
  23. package/dist/completions.js +224 -0
  24. package/dist/config.d.ts +28 -0
  25. package/dist/config.js +98 -0
  26. package/dist/contrast.d.ts +70 -0
  27. package/dist/contrast.js +93 -0
  28. package/dist/execute.d.ts +97 -0
  29. package/dist/execute.js +433 -0
  30. package/dist/exit-code.d.ts +18 -0
  31. package/dist/exit-code.js +12 -0
  32. package/dist/help.d.ts +22 -0
  33. package/dist/help.js +158 -0
  34. package/dist/index.d.ts +16 -1
  35. package/dist/index.js +9 -2
  36. package/dist/manifest.d.ts +192 -0
  37. package/dist/manifest.js +55 -0
  38. package/dist/mcp.d.ts +40 -0
  39. package/dist/mcp.js +111 -0
  40. package/dist/names.d.ts +5 -0
  41. package/dist/names.js +6 -0
  42. package/dist/pkg.d.ts +5 -0
  43. package/dist/pkg.js +21 -0
  44. package/dist/precedence.d.ts +55 -0
  45. package/dist/precedence.js +100 -0
  46. package/dist/runtime.d.ts +27 -0
  47. package/dist/runtime.js +17 -0
  48. package/dist/schema.d.ts +71 -0
  49. package/dist/schema.js +108 -0
  50. package/dist/testing-helpers.d.ts +62 -0
  51. package/dist/testing-helpers.js +110 -0
  52. package/dist/testing.d.ts +10 -0
  53. package/dist/testing.js +3 -0
  54. package/dist/validate.d.ts +27 -0
  55. package/dist/validate.js +133 -0
  56. package/dist/yargs-burgee.d.ts +50 -0
  57. package/dist/yargs-burgee.js +104 -0
  58. package/dist/yargs-cliui.d.ts +56 -0
  59. package/dist/yargs-cliui.js +421 -0
  60. package/dist/yargs-command.d.ts +82 -0
  61. package/dist/yargs-command.js +414 -0
  62. package/dist/yargs-completion.d.ts +41 -0
  63. package/dist/yargs-completion.js +271 -0
  64. package/dist/yargs-factory.d.ts +193 -0
  65. package/dist/yargs-factory.js +1606 -0
  66. package/dist/yargs-helpers.d.ts +6 -0
  67. package/dist/yargs-helpers.js +2 -0
  68. package/dist/yargs-middleware.d.ts +32 -0
  69. package/dist/yargs-middleware.js +81 -0
  70. package/dist/yargs-parser.d.ts +41 -0
  71. package/dist/yargs-parser.js +929 -0
  72. package/dist/yargs-shim.d.ts +54 -0
  73. package/dist/yargs-shim.js +84 -0
  74. package/dist/yargs-usage.d.ts +42 -0
  75. package/dist/yargs-usage.js +479 -0
  76. package/dist/yargs-utils.d.ts +33 -0
  77. package/dist/yargs-utils.js +209 -0
  78. package/dist/yargs-validation.d.ts +26 -0
  79. package/dist/yargs-validation.js +261 -0
  80. package/dist/yargs-y18n.d.ts +21 -0
  81. package/dist/yargs-y18n.js +117 -0
  82. package/dist/yargs.d.ts +6 -0
  83. package/dist/yargs.js +8 -0
  84. package/locales/be.json +46 -0
  85. package/locales/cs.json +51 -0
  86. package/locales/de.json +46 -0
  87. package/locales/en.json +55 -0
  88. package/locales/es.json +46 -0
  89. package/locales/fi.json +49 -0
  90. package/locales/fr.json +53 -0
  91. package/locales/he.json +55 -0
  92. package/locales/hi.json +49 -0
  93. package/locales/hu.json +46 -0
  94. package/locales/id.json +50 -0
  95. package/locales/it.json +46 -0
  96. package/locales/ja.json +51 -0
  97. package/locales/ka.json +55 -0
  98. package/locales/ko.json +49 -0
  99. package/locales/nb.json +44 -0
  100. package/locales/nl.json +49 -0
  101. package/locales/nn.json +44 -0
  102. package/locales/pirate.json +13 -0
  103. package/locales/pl.json +49 -0
  104. package/locales/pt.json +45 -0
  105. package/locales/pt_BR.json +48 -0
  106. package/locales/ru.json +51 -0
  107. package/locales/th.json +46 -0
  108. package/locales/tr.json +48 -0
  109. package/locales/uk_UA.json +51 -0
  110. package/locales/uz.json +52 -0
  111. package/locales/zh_CN.json +48 -0
  112. package/locales/zh_TW.json +51 -0
  113. package/package.json +61 -8
  114. package/dist/index.js.map +0 -1
@@ -0,0 +1,97 @@
1
+ import { type ArgumentSpec, type Effects, type Example, Manifest, type OptionSpec, type Relation, type RunContext } from './manifest.js';
2
+ export interface CommandContext<O> extends Omit<RunContext, 'options'> {
3
+ options: O;
4
+ }
5
+ type Scalar<S extends OptionSpec> = S['type'] extends 'number' ? number : S['type'] extends 'boolean' ? boolean : S extends {
6
+ choices: readonly (infer C)[];
7
+ } ? C : string;
8
+ type Many<S extends OptionSpec, V> = S extends {
9
+ multiple: true;
10
+ } ? V[] : V;
11
+ type Present<S extends OptionSpec> = S extends {
12
+ required: true;
13
+ } ? true : S extends {
14
+ default: unknown;
15
+ } ? true : false;
16
+ /** The handler's `options`, derived from the declaration (S1): `choices` become a union, `multiple` an array, `number` a number. */
17
+ export type InferOptions<S extends Record<string, OptionSpec>> = {
18
+ [K in keyof S]: Present<S[K]> extends true ? Many<S[K], Scalar<S[K]>> : Many<S[K], Scalar<S[K]>> | undefined;
19
+ };
20
+ export type OptionSpecs = Record<string, OptionSpec>;
21
+ export type AnyCommand = Command<any>;
22
+ export interface Command<S extends OptionSpecs = OptionSpecs> {
23
+ name: string;
24
+ description?: string;
25
+ /** Shown in command lists instead of the description. */
26
+ summary?: string;
27
+ /** Declared once (S1); the handler's `options` type is derived from it. */
28
+ options?: S;
29
+ arguments?: ArgumentSpec[];
30
+ examples?: Example[];
31
+ /** Heading this command is listed under in its parent's help. */
32
+ group?: string;
33
+ epilogue?: string;
34
+ hidden?: boolean;
35
+ deprecated?: boolean | string;
36
+ /** What running it does to the world (N6). Declaring it is what exposes the command as an MCP tool (N2). */
37
+ effects?: Effects;
38
+ /** Relationships between options, validated before choices and the handler (S2, S6). */
39
+ relations?: readonly Relation[];
40
+ /** Absent on a group that only holds subcommands. `NoInfer`: the spec fixes S, the handler only reads it. */
41
+ run?: (ctx: CommandContext<InferOptions<NoInfer<S>>>) => unknown;
42
+ commands?: AnyCommand[];
43
+ }
44
+ export interface Program {
45
+ name: string;
46
+ version?: string;
47
+ description?: string;
48
+ /** Options read `PREFIX_OPTION_NAME` from the environment unless they name their own variable (V2). */
49
+ envPrefix?: string;
50
+ /** Characters of `--schema` output above which it is summarised (N13); 48,000 by default. */
51
+ schemaBudget?: number;
52
+ /**
53
+ * Opt into config discovery (V6): `--config <path>` > `NAME_CONFIG` > `./name.config.{json,mjs,js,cjs}`
54
+ * > `package.json#name` > the user config directory; `true` uses the program's name.
55
+ */
56
+ config?: boolean | {
57
+ name: string;
58
+ };
59
+ commands: AnyCommand[];
60
+ }
61
+ export declare function defineCommand<const S extends OptionSpecs = OptionSpecs>(command: Command<S>): Command<S>;
62
+ /** A native multi-command program. The manifest it builds is the same one the façades fill. */
63
+ export declare function defineProgram(program: Program): Manifest;
64
+ export interface RunOptions {
65
+ argv?: string[];
66
+ /** Read only by `--mcp`, which serves JSON-RPC over it. */
67
+ stdin?: NodeJS.ReadableStream;
68
+ /** The environment env-bound options read from. Injected by the harness; the process's own otherwise. */
69
+ env?: Record<string, string | undefined>;
70
+ /** `columns` is read when present, so help wraps to the terminal (H3); `isTTY` feeds agent detection (N12). */
71
+ stdout?: {
72
+ write: (s: string) => unknown;
73
+ columns?: number;
74
+ isTTY?: boolean;
75
+ };
76
+ stderr?: {
77
+ write: (s: string) => unknown;
78
+ };
79
+ /** Receives the E1 code. The default calls process.exit; an injected one may simply record it. */
80
+ exit?: (code: number) => void;
81
+ /** Where config discovery starts; the process's own otherwise. */
82
+ cwd?: string;
83
+ /** The entry file, whose nearest package.json owns the program's version (V4); `process.argv[1]` otherwise. */
84
+ entry?: string;
85
+ }
86
+ /** The part of argv the parser will read as options: everything before `--`. */
87
+ export declare function beforeTerminator(argv: readonly string[]): readonly string[];
88
+ export declare function execute(manifest: Manifest, opts?: RunOptions & {
89
+ root?: string[];
90
+ from?: 'node' | 'user';
91
+ }): Promise<void>;
92
+ /**
93
+ * The one-file entry: a single command, or a program from `defineProgram`. Both go
94
+ * through `execute`, so there is exactly one code path from argv to exit.
95
+ */
96
+ export declare function run<S extends OptionSpecs>(target: Command<S> | Manifest, opts?: RunOptions): Promise<void>;
97
+ export {};
@@ -0,0 +1,433 @@
1
+ import { dirname } from 'node:path';
2
+ import { parseArgs } from 'node:util';
3
+ import { detectAgent } from './agent.js';
4
+ import { ExitCode, isExitCode } from './exit-code.js';
5
+ import { renderHelp } from './help.js';
6
+ import { Manifest } from './manifest.js';
7
+ import { serveMcp } from './mcp.js';
8
+ import { camel, kebab } from './names.js';
9
+ import { nearestPackage } from './pkg.js';
10
+ import { ConfigError, explain, resolve as resolveLayers } from './precedence.js';
11
+ import { commandSchemaOf, schemaOf, summaryOf } from './schema.js';
12
+ import { checkDefinition, checkRelations, coerce, UsageError } from './validate.js';
13
+ function helpFields(c) {
14
+ const node = {};
15
+ if (c.description !== undefined)
16
+ node.description = c.description;
17
+ if (c.summary !== undefined)
18
+ node.summary = c.summary;
19
+ if (c.arguments !== undefined)
20
+ node.arguments = c.arguments;
21
+ if (c.examples !== undefined)
22
+ node.examples = c.examples;
23
+ if (c.group !== undefined)
24
+ node.group = c.group;
25
+ if (c.epilogue !== undefined)
26
+ node.epilogue = c.epilogue;
27
+ if (c.hidden !== undefined)
28
+ node.hidden = c.hidden;
29
+ if (c.deprecated !== undefined)
30
+ node.deprecated = c.deprecated;
31
+ if (c.effects !== undefined)
32
+ node.effects = c.effects;
33
+ if (c.relations !== undefined)
34
+ node.relations = c.relations;
35
+ return node;
36
+ }
37
+ const RESERVED = new Set(['json', 'help', 'schema', 'mcp', 'version', 'explain']);
38
+ export function defineCommand(command) {
39
+ for (const name of Object.keys(command.options ?? {})) {
40
+ if (RESERVED.has(name) || RESERVED.has(kebab(name)))
41
+ throw new Error(`burgee: option "${name}" is reserved and cannot be redefined`);
42
+ }
43
+ checkDefinition(command.name, command.options ?? {});
44
+ return command;
45
+ }
46
+ function addTree(manifest, parent, commands) {
47
+ for (const c of commands) {
48
+ const path = [...parent, c.name];
49
+ manifest.add({
50
+ path,
51
+ ...helpFields(c),
52
+ options: c.options ?? {},
53
+ ...(c.run === undefined ? {} : { run: c.run }),
54
+ });
55
+ if (c.commands !== undefined)
56
+ addTree(manifest, path, c.commands);
57
+ }
58
+ }
59
+ export function defineProgram(program) {
60
+ const manifest = new Manifest();
61
+ manifest.rootPath = [program.name];
62
+ if (program.version !== undefined)
63
+ manifest.version = program.version;
64
+ if (program.envPrefix !== undefined)
65
+ manifest.envPrefix = program.envPrefix;
66
+ if (program.schemaBudget !== undefined)
67
+ manifest.schemaBudget = program.schemaBudget;
68
+ if (program.config === true)
69
+ manifest.config = { name: program.name };
70
+ else if (typeof program.config === 'object' && program.config !== null)
71
+ manifest.config = program.config;
72
+ manifest.add({ path: [program.name], ...(program.description === undefined ? {} : { description: program.description }), options: {} });
73
+ addTree(manifest, [program.name], program.commands);
74
+ return manifest;
75
+ }
76
+ class ActionRequired extends Error {
77
+ spec;
78
+ constructor(spec) {
79
+ super(spec.message);
80
+ this.spec = spec;
81
+ }
82
+ }
83
+ const actionRequired = (spec) => {
84
+ throw new ActionRequired(spec);
85
+ };
86
+ class ExitSignal extends Error {
87
+ code;
88
+ constructor(code) {
89
+ super(`exit ${code}`);
90
+ this.code = code;
91
+ }
92
+ }
93
+ function isPlainObject(value) {
94
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
95
+ }
96
+ function leaf(value) {
97
+ if (value === undefined || value === null)
98
+ return '';
99
+ return typeof value === 'string' ? value : JSON.stringify(value);
100
+ }
101
+ function render(value) {
102
+ if (value === undefined || value === null)
103
+ return '';
104
+ if (typeof value === 'string')
105
+ return value;
106
+ if (Array.isArray(value))
107
+ return value.map((v) => leaf(v)).join('\n');
108
+ if (isPlainObject(value)) {
109
+ return Object.entries(value)
110
+ .map(([k, v]) => `${k}: ${leaf(v)}`)
111
+ .join('\n');
112
+ }
113
+ return String(value);
114
+ }
115
+ function toParseConfig(specs, withConfig) {
116
+ const config = { json: { type: 'boolean' }, help: { type: 'boolean' }, version: { type: 'boolean' }, explain: { type: 'string' } };
117
+ if (withConfig) {
118
+ config['config'] = { type: 'string' };
119
+ config['no-config'] = { type: 'boolean' };
120
+ }
121
+ for (const [name, spec] of Object.entries(specs)) {
122
+ config[kebab(name)] = {
123
+ type: spec.type === 'boolean' ? 'boolean' : 'string',
124
+ ...(spec.short === undefined ? {} : { short: spec.short }),
125
+ ...(spec.multiple === true ? { multiple: true } : {}),
126
+ };
127
+ }
128
+ return config;
129
+ }
130
+ function canonical(values) {
131
+ return Object.fromEntries(Object.entries(values).map(([k, v]) => [camel(k), v]));
132
+ }
133
+ function packageLayer(pkg, name) {
134
+ if (pkg === undefined || name === undefined)
135
+ return undefined;
136
+ const field = pkg.data[name];
137
+ return typeof field === 'object' && field !== null && !Array.isArray(field) ? { path: pkg.path, data: field } : undefined;
138
+ }
139
+ async function configLayers(name, values, io) {
140
+ const { discover } = await import('./config.js');
141
+ const explicit = values['config'];
142
+ const disabled = values['noConfig'] === true;
143
+ const loaded = await discover({ name, cwd: io.cwd, env: io.env, ...(typeof explicit === 'string' ? { explicit } : {}), disabled });
144
+ const out = {};
145
+ if (loaded !== undefined)
146
+ out.config = { path: loaded.chain.join(' ← '), data: loaded.data };
147
+ const pkg = disabled ? undefined : packageLayer(io.pkg, name);
148
+ if (pkg !== undefined)
149
+ out.pkg = pkg;
150
+ return out;
151
+ }
152
+ async function resolveValues(manifest, specs, values, io) {
153
+ const layers = { flags: values, env: io.env };
154
+ if (manifest.envPrefix !== undefined)
155
+ layers.envPrefix = manifest.envPrefix;
156
+ if (manifest.config !== undefined)
157
+ Object.assign(layers, await configLayers(manifest.config.name, values, io));
158
+ const resolution = resolveLayers(specs, layers);
159
+ const out = { values: resolution.values, provenance: resolution.provenance };
160
+ const asked = values['explain'];
161
+ if (typeof asked === 'string')
162
+ out.explainText = explain(asked, resolution);
163
+ for (const [name, spec] of Object.entries(specs)) {
164
+ if (out.values[name] === undefined && spec.required === true && out.explainText === undefined) {
165
+ throw new UsageError(`missing required option --${kebab(name)}`, `pass --${kebab(name)} <value>`);
166
+ }
167
+ }
168
+ return out;
169
+ }
170
+ function splitPositionals(tokens) {
171
+ const positionals = [];
172
+ const passthrough = [];
173
+ let after = false;
174
+ for (const token of tokens) {
175
+ const { kind } = token;
176
+ const value = 'value' in token ? token.value : undefined;
177
+ if (kind === 'option-terminator')
178
+ after = true;
179
+ else if (kind === 'positional' && typeof value === 'string')
180
+ (after ? passthrough : positionals).push(value);
181
+ }
182
+ return { positionals, passthrough };
183
+ }
184
+ function isParseArgsFailure(cause) {
185
+ if (!(cause instanceof Error))
186
+ return false;
187
+ const { code } = cause;
188
+ return typeof code === 'string' && code.startsWith('ERR_PARSE_ARGS_');
189
+ }
190
+ function exitSignal(cause) {
191
+ const code = cause?.code;
192
+ return typeof code === 'number' && isExitCode(code) ? code : undefined;
193
+ }
194
+ const SINGLE_DASH_WORD = /^-([a-zA-Z][\w-]+)(?:=.*)?$/;
195
+ function singleDashHint(argv) {
196
+ for (const token of argv) {
197
+ if (token === '--')
198
+ return undefined;
199
+ const found = SINGLE_DASH_WORD.exec(token);
200
+ if (found?.[1] !== undefined)
201
+ return `did you mean --${found[1]}? a single dash introduces one-letter options`;
202
+ }
203
+ return undefined;
204
+ }
205
+ function describeFailure(cause, argv) {
206
+ const signal = exitSignal(cause);
207
+ if (signal !== undefined)
208
+ return { code: signal, message: '', silent: true };
209
+ const message = cause instanceof Error ? cause.message : String(cause);
210
+ if (cause instanceof ActionRequired)
211
+ return { code: ExitCode.CANCELLED, message, action: cause.spec, ...(cause.spec.hint === undefined ? {} : { hint: cause.spec.hint }) };
212
+ if (cause instanceof UsageError) {
213
+ return { code: ExitCode.USAGE, message, ...(cause.hint === undefined ? {} : { hint: cause.hint }) };
214
+ }
215
+ if (cause instanceof ConfigError) {
216
+ return { code: ExitCode.CONFIG, message, ...(cause.hint === undefined ? {} : { hint: cause.hint }) };
217
+ }
218
+ if (isParseArgsFailure(cause)) {
219
+ return { code: ExitCode.USAGE, message, hint: singleDashHint(argv) ?? 'run --help to see the available options' };
220
+ }
221
+ return { code: ExitCode.RUNTIME, message };
222
+ }
223
+ function textFailure(failure) {
224
+ const hint = failure.hint === undefined ? '' : `hint: ${failure.hint}\n`;
225
+ if (failure.action !== undefined) {
226
+ const next = (failure.action.next ?? []).map((n) => ` ${n.command} ${n.when}\n`).join('');
227
+ return `action required (${failure.action.reason}): ${failure.message}\n${next === '' ? '' : `next:\n${next}`}${hint}`;
228
+ }
229
+ return `error: ${failure.message}\n${hint}`;
230
+ }
231
+ function runnableNext(manifest, spec, json) {
232
+ const program = manifest.rootPath.join(' ');
233
+ return (spec.next ?? []).map((n) => ({ command: `${program} ${n.command}${json && !n.command.includes('--json') ? ' --json' : ''}`, when: n.when }));
234
+ }
235
+ const HELP_FLAGS = new Set(['--help', '-h']);
236
+ const HELP_WIDTH = 100;
237
+ const processExit = (code) => process.exit(code);
238
+ export function beforeTerminator(argv) {
239
+ const at = argv.indexOf('--');
240
+ return at === -1 ? argv : argv.slice(0, at);
241
+ }
242
+ function rootNode(manifest, root) {
243
+ return manifest.find(root) ?? { path: root, options: {} };
244
+ }
245
+ function unresolved({ manifest, root, io: { width } }, argv, at) {
246
+ const node = at ?? rootNode(manifest, root);
247
+ const typed = argv.slice(node.path.length - root.length);
248
+ if (typed.length > 0 && HELP_FLAGS.has(typed[0] ?? ''))
249
+ return { text: renderHelp(manifest, node, { width }), code: ExitCode.OK };
250
+ if (typed.length === 0)
251
+ return { text: renderHelp(manifest, node, { width }), code: ExitCode.USAGE };
252
+ throw new UsageError(`unknown command "${typed[0] ?? ''}"`, 'run --help to see the available commands');
253
+ }
254
+ async function completion(manifest, argv, io) {
255
+ if (argv[0] !== 'completion' || manifest.find([...manifest.rootPath, 'completion']) !== undefined)
256
+ return false;
257
+ const { renderCompletion, renderFigSpec, SHELLS } = await import('./completions.js');
258
+ const shell = argv[1] ?? '';
259
+ if (shell === 'fig') {
260
+ io.out.write(`${JSON.stringify(renderFigSpec(manifest), null, 2)}\n`);
261
+ return true;
262
+ }
263
+ const known = SHELLS.find((s) => s === shell);
264
+ if (known === undefined)
265
+ throw new UsageError(`unknown shell "${shell}"`, `completion ${SHELLS.join('|')}|fig`);
266
+ io.out.write(renderCompletion(manifest, known));
267
+ return true;
268
+ }
269
+ async function surface(manifest, argv, io) {
270
+ const head = beforeTerminator(argv);
271
+ if (await completion(manifest, argv, io))
272
+ return true;
273
+ if (argv[0] === 'help') {
274
+ io.out.write(helpCommand(manifest, argv.slice(1), manifest.rootPath, io.width));
275
+ return true;
276
+ }
277
+ if (head.includes('--schema')) {
278
+ io.out.write(`${JSON.stringify(schemaSurface(manifest, argv), null, 2)}\n`);
279
+ return true;
280
+ }
281
+ if (head[0] === '--mcp') {
282
+ const invoke = async (args) => {
283
+ const out = [];
284
+ const err = [];
285
+ let code = 0;
286
+ await execute(manifest, {
287
+ argv: args,
288
+ env: io.env,
289
+ stdout: { write: (s) => out.push(s) },
290
+ stderr: { write: (s) => err.push(s) },
291
+ exit: (c) => {
292
+ code = c;
293
+ },
294
+ });
295
+ return { stdout: out.join(''), stderr: err.join(''), code };
296
+ };
297
+ await serveMcp(manifest, { input: io.stdin, output: io.out, invoke });
298
+ return true;
299
+ }
300
+ return false;
301
+ }
302
+ const SCHEMA_BUDGET = 48_000;
303
+ function schemaSurface(manifest, argv) {
304
+ const { node } = manifest.resolve(beforeTerminator(argv).filter((a) => a !== '--schema'), manifest.rootPath);
305
+ if (node?.run !== undefined)
306
+ return commandSchemaOf(node, manifest.rootPath);
307
+ const full = schemaOf(manifest);
308
+ const budget = manifest.schemaBudget ?? SCHEMA_BUDGET;
309
+ return JSON.stringify(full).length <= budget ? full : summaryOf(manifest, budget);
310
+ }
311
+ function helpCommand(manifest, argv, root, width) {
312
+ const { node } = manifest.resolve(argv, root);
313
+ return renderHelp(manifest, node ?? rootNode(manifest, root), { width });
314
+ }
315
+ function changedOf(node, data) {
316
+ const value = isPlainObject(data) ? data['changed'] : undefined;
317
+ if (typeof value === 'boolean')
318
+ return value;
319
+ if (node.effects === 'idempotent') {
320
+ throw new Error(`"${node.path.slice(1).join(' ')}" is idempotent and must report changed: true | false in its result (N7)`);
321
+ }
322
+ return undefined;
323
+ }
324
+ function versionOf(manifest, io) {
325
+ const declared = manifest.version ?? (typeof io.pkg?.data['version'] === 'string' ? io.pkg.data['version'] : undefined);
326
+ if (declared === undefined)
327
+ throw new ConfigError('no version declared', 'pass version to defineProgram, or set "version" in the owning package.json');
328
+ return declared;
329
+ }
330
+ async function dispatch(manifest, { node, rest, name }, io) {
331
+ const parsed = parseArgs({ args: rest, options: toParseConfig(node.options, manifest.config !== undefined), allowPositionals: true, strict: true, tokens: true });
332
+ const flags = canonical(parsed.values);
333
+ const json = flags.json === true;
334
+ if (flags.help === true)
335
+ return { json, text: renderHelp(manifest, node, { width: io.width }) };
336
+ if (flags.version === true)
337
+ return { json, text: `${versionOf(manifest, io)}\n` };
338
+ const resolved = await resolveValues(manifest, node.options, flags, io);
339
+ if (resolved.explainText !== undefined)
340
+ return { json, text: resolved.explainText };
341
+ const { provenance } = resolved;
342
+ checkRelations(node.relations, resolved.values, provenance);
343
+ const values = await coerce(node.options, resolved.values);
344
+ const { positionals, passthrough } = splitPositionals(parsed.tokens);
345
+ await manifest.fire('preRun', name, values);
346
+ const exit = (code) => {
347
+ io.exit(code);
348
+ throw new ExitSignal(code);
349
+ };
350
+ const detection = detectAgent(io.env, io.tty);
351
+ const data = await node.run({ options: values, positionals, passthrough, env: io.env, exit, actionRequired, ...detection });
352
+ await manifest.fire('postRun', name, values);
353
+ const changed = changedOf(node, data);
354
+ return { json, data, provenance, ...(changed === undefined ? {} : { changed }) };
355
+ }
356
+ function emit(io, outcome) {
357
+ if (outcome.text !== undefined) {
358
+ io.out.write(outcome.text);
359
+ return io.exit(ExitCode.OK);
360
+ }
361
+ const meta = { provenance: outcome.provenance ?? {}, ...(outcome.changed === undefined ? {} : { changed: outcome.changed }) };
362
+ const envelope = { ok: true, data: outcome.data, meta };
363
+ io.out.write(outcome.json ? `${JSON.stringify(envelope)}\n` : `${render(outcome.data)}\n`);
364
+ return io.exit(ExitCode.OK);
365
+ }
366
+ async function report(cause, { manifest, io, argv, json, name }) {
367
+ const failure = describeFailure(cause, argv);
368
+ if (failure.silent === true)
369
+ return io.exit(failure.code);
370
+ await manifest.fire('onError', name, {});
371
+ if (failure.action !== undefined) {
372
+ const next = runnableNext(manifest, failure.action, json);
373
+ const rendered = { ...failure, action: { ...failure.action, next } };
374
+ const body = { ok: false, status: 'action_required', reason: failure.action.reason, message: failure.message, next, hint: failure.hint, error: { code: failure.code, message: failure.message } };
375
+ io.err.write(json ? `${JSON.stringify(body)}\n` : textFailure(rendered));
376
+ return io.exit(failure.code);
377
+ }
378
+ const body = { code: failure.code, message: failure.message, hint: failure.hint };
379
+ io.err.write(json ? `${JSON.stringify({ ok: false, error: body })}\n` : textFailure(failure));
380
+ return io.exit(failure.code);
381
+ }
382
+ function ioOf(opts) {
383
+ const out = opts.stdout ?? process.stdout;
384
+ return {
385
+ out,
386
+ err: opts.stderr ?? process.stderr,
387
+ env: opts.env ?? process.env,
388
+ exit: opts.exit ?? processExit,
389
+ width: out.columns ?? HELP_WIDTH,
390
+ stdin: opts.stdin ?? process.stdin,
391
+ cwd: opts.cwd ?? process.cwd(),
392
+ pkg: nearestPackage(dirname(opts.entry ?? process.argv[1] ?? process.cwd())),
393
+ tty: out.isTTY === true,
394
+ };
395
+ }
396
+ export async function execute(manifest, opts = {}) {
397
+ const io = ioOf(opts);
398
+ const raw = opts.argv ?? process.argv;
399
+ const argv = opts.argv === undefined || opts.from === 'node' ? raw.slice(2) : raw;
400
+ const root = opts.root ?? manifest.rootPath;
401
+ let json = beforeTerminator(argv).includes('--json');
402
+ let name = '';
403
+ try {
404
+ if (await surface(manifest, argv, io))
405
+ return io.exit(ExitCode.OK);
406
+ const { node, rest } = manifest.resolve(argv, root);
407
+ if (node?.run === undefined) {
408
+ const { text, code } = unresolved({ manifest, root, io }, argv, node);
409
+ (code === ExitCode.OK ? io.out : io.err).write(text);
410
+ return io.exit(code);
411
+ }
412
+ name = node.path.slice(root.length).join(' ');
413
+ const outcome = await dispatch(manifest, { node: node, rest, name }, io);
414
+ json = outcome.json;
415
+ return emit(io, outcome);
416
+ }
417
+ catch (cause) {
418
+ return await report(cause, { manifest, io, argv, json, name });
419
+ }
420
+ }
421
+ export async function run(target, opts = {}) {
422
+ if (target instanceof Manifest)
423
+ return await execute(target, opts);
424
+ const manifest = new Manifest();
425
+ manifest.rootPath = [target.name];
426
+ manifest.add({
427
+ path: [target.name],
428
+ ...(target.description === undefined ? {} : { description: target.description }),
429
+ options: target.options ?? {},
430
+ ...(target.run === undefined ? {} : { run: target.run }),
431
+ });
432
+ return await execute(manifest, opts);
433
+ }
@@ -0,0 +1,18 @@
1
+ /** E1 — exit codes are a contract. No other literal may reach `process.exitCode`. */
2
+ export declare const ExitCode: {
3
+ /** Command completed. */
4
+ readonly OK: 0;
5
+ /** The command ran and failed. Never accompanied by help text (E2). */
6
+ readonly RUNTIME: 1;
7
+ /** Bad arguments, unknown command, missing flag, prompt needed in a non-TTY (P2). */
8
+ readonly USAGE: 2;
9
+ /** Config file or environment could not be loaded or validated (V1). */
10
+ readonly CONFIG: 3;
11
+ /** The user or caller cancelled. */
12
+ readonly CANCELLED: 4;
13
+ /** SIGINT after the terminal was restored (E5). */
14
+ readonly SIGINT: 130;
15
+ };
16
+ export type ExitCode = (typeof ExitCode)[keyof typeof ExitCode];
17
+ /** True for the six codes in the contract and nothing else. */
18
+ export declare function isExitCode(n: unknown): n is ExitCode;
@@ -0,0 +1,12 @@
1
+ export const ExitCode = {
2
+ OK: 0,
3
+ RUNTIME: 1,
4
+ USAGE: 2,
5
+ CONFIG: 3,
6
+ CANCELLED: 4,
7
+ SIGINT: 130,
8
+ };
9
+ const CODES = new Set(Object.values(ExitCode));
10
+ export function isExitCode(n) {
11
+ return typeof n === 'number' && CODES.has(n);
12
+ }
package/dist/help.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Help, rendered from the manifest and nothing else (H1). One layout for every node,
3
+ * so a subcommand's help has everything the root's has (yargs #1500, #1331, #1025).
4
+ *
5
+ * Section order is fixed (R2): usage, description, arguments, options, global options,
6
+ * commands (grouped, yargs #684), examples, environment, epilogue. Empty sections are
7
+ * omitted. Width comes from the caller — the runtime, in practice (H3) — default 100.
8
+ */
9
+ import type { CommandNode, Manifest } from './manifest.js';
10
+ export interface HelpOptions {
11
+ /** Columns available; 100 when unknown, never `process.stdout` directly (H3). */
12
+ width?: number;
13
+ /** Show type hints such as `[string]`; off by default (H6). */
14
+ verbose?: boolean;
15
+ }
16
+ /** Word-wrap one paragraph; lines the author indented are kept verbatim (yargs #2120). */
17
+ export declare function wrap(text: string, width: number): string[];
18
+ /**
19
+ * Render help for one node — a runnable command, a group, or both — as text.
20
+ * Deterministic for a given node and width; a snapshot suite pins it.
21
+ */
22
+ export declare function renderHelp(manifest: Manifest, node: CommandNode, opts?: HelpOptions): string;