@andrei.fyi/picocli 0.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.
- package/README.md +357 -0
- package/dist/create-eQnnIhZu.d.cts +87 -0
- package/dist/create-eQnnIhZu.d.ts +87 -0
- package/dist/index.cjs +1175 -0
- package/dist/index.d.cts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +1166 -0
- package/dist/mcp/index.cjs +507 -0
- package/dist/mcp/index.d.cts +30 -0
- package/dist/mcp/index.d.ts +30 -0
- package/dist/mcp/index.js +502 -0
- package/dist/testing/testkit.cjs +115 -0
- package/dist/testing/testkit.d.cts +23 -0
- package/dist/testing/testkit.d.ts +23 -0
- package/dist/testing/testkit.js +112 -0
- package/package.json +94 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1175 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var zod = require('zod');
|
|
6
|
+
var async_hooks = require('async_hooks');
|
|
7
|
+
|
|
8
|
+
// src/flags/tokenizer.ts
|
|
9
|
+
var readBooleanFlagValue = (value) => value !== "false" && value !== "0" && value !== "no";
|
|
10
|
+
var isLongFlagToken = (token) => {
|
|
11
|
+
return token.startsWith("--") && token.length > 2;
|
|
12
|
+
};
|
|
13
|
+
var pushFlag = (flags, name, value) => {
|
|
14
|
+
const values = flags.get(name);
|
|
15
|
+
if (values) values.push(value);
|
|
16
|
+
else flags.set(name, [value]);
|
|
17
|
+
};
|
|
18
|
+
var readNextValueToken = (argv, index) => {
|
|
19
|
+
const next = argv[index + 1];
|
|
20
|
+
if (next === void 0 || next === "--" || isLongFlagToken(next)) return void 0;
|
|
21
|
+
return { nextIndex: index + 1, value: next };
|
|
22
|
+
};
|
|
23
|
+
var parseLongFlagToken = (token) => {
|
|
24
|
+
const body = token.slice(2);
|
|
25
|
+
const eq = body.indexOf("=");
|
|
26
|
+
return {
|
|
27
|
+
name: eq === -1 ? body : body.slice(0, eq),
|
|
28
|
+
inlineValue: eq === -1 ? void 0 : body.slice(eq + 1)
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
var readKnownLongFlag = (flag, inlineValue, argv, index, state) => {
|
|
32
|
+
if (flag.isBoolean) {
|
|
33
|
+
pushFlag(state.flags, flag.name, inlineValue === void 0 ? true : readBooleanFlagValue(inlineValue));
|
|
34
|
+
return index;
|
|
35
|
+
}
|
|
36
|
+
if (inlineValue !== void 0) {
|
|
37
|
+
pushFlag(state.flags, flag.name, inlineValue);
|
|
38
|
+
return index;
|
|
39
|
+
}
|
|
40
|
+
const spaced = readNextValueToken(argv, index);
|
|
41
|
+
if (spaced) {
|
|
42
|
+
pushFlag(state.flags, flag.name, spaced.value);
|
|
43
|
+
return spaced.nextIndex;
|
|
44
|
+
}
|
|
45
|
+
state.missing.push(flag.name);
|
|
46
|
+
return index;
|
|
47
|
+
};
|
|
48
|
+
var readLongFlagToken = (token, argv, index, model, state) => {
|
|
49
|
+
const { name, inlineValue } = parseLongFlagToken(token);
|
|
50
|
+
if (name.startsWith("no-")) {
|
|
51
|
+
const negated = model.negatable(name);
|
|
52
|
+
if (negated) {
|
|
53
|
+
pushFlag(state.flags, negated, false);
|
|
54
|
+
return index;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const canonical = model.long(name);
|
|
58
|
+
if (canonical) {
|
|
59
|
+
return readKnownLongFlag(
|
|
60
|
+
{ name: canonical, isBoolean: model.isBoolean(canonical) },
|
|
61
|
+
inlineValue,
|
|
62
|
+
argv,
|
|
63
|
+
index,
|
|
64
|
+
state
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
state.unknown.push(`--${name}`);
|
|
68
|
+
return index;
|
|
69
|
+
};
|
|
70
|
+
var tokenizeArgv = (argv, model) => {
|
|
71
|
+
const flags = /* @__PURE__ */ new Map();
|
|
72
|
+
const state = {
|
|
73
|
+
flags,
|
|
74
|
+
positionals: [],
|
|
75
|
+
rest: [],
|
|
76
|
+
unknown: [],
|
|
77
|
+
missing: []
|
|
78
|
+
};
|
|
79
|
+
let afterDashDash = false;
|
|
80
|
+
for (let i = 0; i < argv.length; i++) {
|
|
81
|
+
const tok = argv[i];
|
|
82
|
+
if (tok === void 0) continue;
|
|
83
|
+
if (afterDashDash) {
|
|
84
|
+
state.rest.push(tok);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (tok === "--") {
|
|
88
|
+
afterDashDash = true;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (tok.startsWith("--")) {
|
|
92
|
+
i = readLongFlagToken(tok, argv, i, model, state);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
state.positionals.push(tok);
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
flags,
|
|
99
|
+
positionals: state.positionals,
|
|
100
|
+
rest: state.rest,
|
|
101
|
+
unknown: state.unknown,
|
|
102
|
+
missing: state.missing
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// src/schemas/zod.ts
|
|
107
|
+
var WRAPPERS = /* @__PURE__ */ new Set(["catch", "default", "nonoptional", "nullable", "optional", "prefault", "readonly"]);
|
|
108
|
+
var toKebabCase = (s) => s.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
109
|
+
var getShape = (obj) => {
|
|
110
|
+
if (!obj) return void 0;
|
|
111
|
+
return obj.shape;
|
|
112
|
+
};
|
|
113
|
+
var getBaseType = (schema) => {
|
|
114
|
+
let cur = schema;
|
|
115
|
+
for (let i = 0; i < 12; i++) {
|
|
116
|
+
const def = cur?._zod?.def;
|
|
117
|
+
if (!def?.type) return void 0;
|
|
118
|
+
if (WRAPPERS.has(def.type)) {
|
|
119
|
+
cur = def.innerType;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
return def.type;
|
|
123
|
+
}
|
|
124
|
+
return void 0;
|
|
125
|
+
};
|
|
126
|
+
var isBooleanOption = (s) => getBaseType(s) === "boolean";
|
|
127
|
+
var isArrayOption = (s) => getBaseType(s) === "array";
|
|
128
|
+
var isOptional = (s) => {
|
|
129
|
+
let cur = s;
|
|
130
|
+
for (let i = 0; i < 12; i++) {
|
|
131
|
+
const def = cur?._zod?.def;
|
|
132
|
+
if (!def?.type) return false;
|
|
133
|
+
if (def.type === "default" || def.type === "optional" || def.type === "prefault" || def.type === "catch") {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
if (WRAPPERS.has(def.type)) {
|
|
137
|
+
cur = def.innerType;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
};
|
|
144
|
+
var getDescription = (s) => {
|
|
145
|
+
const meta = s?.meta?.();
|
|
146
|
+
return meta?.description;
|
|
147
|
+
};
|
|
148
|
+
var getShapeKeys = (obj) => {
|
|
149
|
+
return Object.keys(getShape(obj) ?? {});
|
|
150
|
+
};
|
|
151
|
+
var getField = (obj, key) => {
|
|
152
|
+
return getShape(obj)?.[key];
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// src/schemas/object.ts
|
|
156
|
+
var overrideDateJsonSchema = (ctx) => {
|
|
157
|
+
if (ctx.zodSchema?._zod?.def?.type === "date") {
|
|
158
|
+
const jsonSchema = ctx.jsonSchema;
|
|
159
|
+
jsonSchema.type = "string";
|
|
160
|
+
jsonSchema.format = "date-time";
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
var mergeObjects = (parts) => {
|
|
164
|
+
const shapes = parts.map(getShape).filter((shape) => shape !== void 0);
|
|
165
|
+
if (shapes.length === 0) return void 0;
|
|
166
|
+
return zod.z.object(Object.assign({}, ...shapes));
|
|
167
|
+
};
|
|
168
|
+
var getDuplicateKeys = (parts) => {
|
|
169
|
+
const seen = /* @__PURE__ */ new Set();
|
|
170
|
+
const duplicate = /* @__PURE__ */ new Set();
|
|
171
|
+
for (const part of parts) {
|
|
172
|
+
const shape = getShape(part);
|
|
173
|
+
for (const key of Object.keys(shape ?? {})) {
|
|
174
|
+
if (seen.has(key)) duplicate.add(key);
|
|
175
|
+
else seen.add(key);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return [...duplicate].sort();
|
|
179
|
+
};
|
|
180
|
+
var getDuplicateKeyMessage = (keys) => {
|
|
181
|
+
return `schema fields must be unique across merged inputs: ${keys.join(", ")}`;
|
|
182
|
+
};
|
|
183
|
+
var toJsonSchema = (schema) => {
|
|
184
|
+
if (!schema) return { type: "object", properties: {}, additionalProperties: false };
|
|
185
|
+
return zod.z.toJSONSchema(schema, {
|
|
186
|
+
unrepresentable: "any",
|
|
187
|
+
override: overrideDateJsonSchema
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// src/command/tree.ts
|
|
192
|
+
var isValidCommandName = (name) => name.length > 0 && !isLongFlagToken(name) && name !== "--";
|
|
193
|
+
var commandTreeIssueCache = /* @__PURE__ */ new WeakMap();
|
|
194
|
+
var commandChildrenMap = /* @__PURE__ */ new WeakMap();
|
|
195
|
+
var emptyCommandChildren = /* @__PURE__ */ new Map();
|
|
196
|
+
var getCommandAliases = (def) => typeof def.alias === "string" ? [def.alias] : def.alias ?? [];
|
|
197
|
+
var clearCommandTreeIssueCache = (root) => {
|
|
198
|
+
commandTreeIssueCache.delete(root);
|
|
199
|
+
};
|
|
200
|
+
var getCommandChildren = (node) => {
|
|
201
|
+
return commandChildrenMap.get(node) ?? emptyCommandChildren;
|
|
202
|
+
};
|
|
203
|
+
var setCommandChild = (node, name, child) => {
|
|
204
|
+
const children = commandChildrenMap.get(node) ?? /* @__PURE__ */ new Map();
|
|
205
|
+
children.set(name, child);
|
|
206
|
+
commandChildrenMap.set(node, children);
|
|
207
|
+
};
|
|
208
|
+
var createCommandNodeView = (node, def) => {
|
|
209
|
+
const view = { name: node.name, def };
|
|
210
|
+
const children = commandChildrenMap.get(node);
|
|
211
|
+
if (children) commandChildrenMap.set(view, children);
|
|
212
|
+
return view;
|
|
213
|
+
};
|
|
214
|
+
var findCommandTreeIssue = (root) => {
|
|
215
|
+
if (commandTreeIssueCache.has(root)) return commandTreeIssueCache.get(root);
|
|
216
|
+
const walk = (node) => {
|
|
217
|
+
const owner = node.name;
|
|
218
|
+
const seen = /* @__PURE__ */ new Map();
|
|
219
|
+
const children = getCommandChildren(node);
|
|
220
|
+
for (const [name, child] of children) {
|
|
221
|
+
if (!isValidCommandName(name)) return `invalid command name "${name}" under "${owner}"`;
|
|
222
|
+
const existing = seen.get(name);
|
|
223
|
+
if (existing) return `command name "${name}" for "${name}" conflicts with ${existing}`;
|
|
224
|
+
seen.set(name, `command "${name}"`);
|
|
225
|
+
for (const alias of getCommandAliases(child.def)) {
|
|
226
|
+
if (!isValidCommandName(alias)) return `invalid alias "${alias}" for command "${name}"`;
|
|
227
|
+
const conflict = seen.get(alias);
|
|
228
|
+
if (conflict) return `alias "${alias}" for command "${name}" conflicts with ${conflict}`;
|
|
229
|
+
seen.set(alias, `command "${name}"`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
for (const child of children.values()) {
|
|
233
|
+
const issue2 = walk(child);
|
|
234
|
+
if (issue2) return issue2;
|
|
235
|
+
}
|
|
236
|
+
return void 0;
|
|
237
|
+
};
|
|
238
|
+
const issue = walk(root);
|
|
239
|
+
commandTreeIssueCache.set(root, issue);
|
|
240
|
+
return issue;
|
|
241
|
+
};
|
|
242
|
+
var getChildNames = (node) => {
|
|
243
|
+
const names = [];
|
|
244
|
+
for (const [name, child] of getCommandChildren(node)) names.push(name, ...getCommandAliases(child.def));
|
|
245
|
+
return names;
|
|
246
|
+
};
|
|
247
|
+
var findCommandChild = (node, token) => {
|
|
248
|
+
const children = getCommandChildren(node);
|
|
249
|
+
const direct = children.get(token);
|
|
250
|
+
if (direct) return [token, direct];
|
|
251
|
+
for (const [name, child] of children) {
|
|
252
|
+
if (getCommandAliases(child.def).includes(token)) return [name, child];
|
|
253
|
+
}
|
|
254
|
+
return void 0;
|
|
255
|
+
};
|
|
256
|
+
var getCommandChain = (root, commandPath) => {
|
|
257
|
+
const chain = [root];
|
|
258
|
+
let node = root;
|
|
259
|
+
for (const name of commandPath) {
|
|
260
|
+
const child = getCommandChildren(node).get(name);
|
|
261
|
+
if (!child) throw new Error(`internal command path missing: ${commandPath.join(" ")}`);
|
|
262
|
+
chain.push(child);
|
|
263
|
+
node = child;
|
|
264
|
+
}
|
|
265
|
+
return chain;
|
|
266
|
+
};
|
|
267
|
+
var getEffectiveCommandDefinition = (chain) => {
|
|
268
|
+
const node = chain[chain.length - 1];
|
|
269
|
+
if (!node) return {};
|
|
270
|
+
return {
|
|
271
|
+
...node.def,
|
|
272
|
+
options: mergeObjects(chain.map((n) => n.def.options)),
|
|
273
|
+
env: mergeObjects(chain.map((n) => n.def.env))
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
var collectCommandEntries = (root) => {
|
|
277
|
+
const entries = [];
|
|
278
|
+
const walk = (node, commandPath, ancestors) => {
|
|
279
|
+
if (node.def.hidden) return;
|
|
280
|
+
const chain = [...ancestors, node];
|
|
281
|
+
entries.push({ commandPath, node, chain, def: getEffectiveCommandDefinition(chain) });
|
|
282
|
+
for (const [name, child] of getCommandChildren(node)) walk(child, [...commandPath, name], chain);
|
|
283
|
+
};
|
|
284
|
+
walk(root, [], []);
|
|
285
|
+
return entries;
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// src/errors.ts
|
|
289
|
+
var PicocliError = class extends Error {
|
|
290
|
+
code;
|
|
291
|
+
constructor(code, message) {
|
|
292
|
+
super(message);
|
|
293
|
+
this.name = "PicocliError";
|
|
294
|
+
this.code = code;
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
var isPicocliError = (e) => e instanceof PicocliError || typeof e === "object" && e !== null && e.name === "PicocliError";
|
|
298
|
+
var getExitCodeForError = (code) => code === "UNKNOWN" ? 1 : 2;
|
|
299
|
+
|
|
300
|
+
// src/render/format.ts
|
|
301
|
+
var renderError = (code, message, paint) => {
|
|
302
|
+
const prefix = `error (${code}):`;
|
|
303
|
+
return `${paint(prefix, "red", "bold")} ${message}
|
|
304
|
+
`;
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
// src/flags/globals.ts
|
|
308
|
+
var GLOBAL_FLAGS = [
|
|
309
|
+
{ name: "help", bool: true, description: "show help" },
|
|
310
|
+
{ name: "version", bool: true, description: "print version" },
|
|
311
|
+
{ name: "json", bool: true, description: "emit JSON output" },
|
|
312
|
+
{ name: "format", bool: false, description: "output format: pretty | json" },
|
|
313
|
+
{ name: "color", bool: true, description: "force/disable color (--color / --no-color)" },
|
|
314
|
+
{ name: "mcp", bool: true, description: "run as an MCP stdio server" },
|
|
315
|
+
{ name: "llms", bool: true, description: "print a Markdown command manifest" },
|
|
316
|
+
{ name: "schema", bool: true, description: "print input/output JSON Schemas for the command" }
|
|
317
|
+
];
|
|
318
|
+
var FEATURE_GLOBAL_FLAGS = /* @__PURE__ */ new Set(["llms", "mcp", "schema"]);
|
|
319
|
+
var isFeatureGlobalFlag = (name) => {
|
|
320
|
+
return FEATURE_GLOBAL_FLAGS.has(name);
|
|
321
|
+
};
|
|
322
|
+
var getActiveGlobalFlags = (optionKeys, features) => {
|
|
323
|
+
const taken = new Set(optionKeys);
|
|
324
|
+
return GLOBAL_FLAGS.filter((g) => !taken.has(g.name) && (!isFeatureGlobalFlag(g.name) || features[g.name]));
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// src/render/help.ts
|
|
328
|
+
var getArgumentUsage = (args) => {
|
|
329
|
+
const keys = getShapeKeys(args);
|
|
330
|
+
return keys.map((k, i) => {
|
|
331
|
+
const f = getField(args, k);
|
|
332
|
+
const variadic = i === keys.length - 1 && isArrayOption(f);
|
|
333
|
+
const inner = variadic ? `${k}...` : k;
|
|
334
|
+
return isOptional(f) || variadic ? `[${inner}]` : `<${inner}>`;
|
|
335
|
+
}).join(" ");
|
|
336
|
+
};
|
|
337
|
+
var getOptionLines = (options, paint) => {
|
|
338
|
+
return getShapeKeys(options).map((k) => {
|
|
339
|
+
const f = getField(options, k);
|
|
340
|
+
const optionName = `--${toKebabCase(k)}`;
|
|
341
|
+
const names = ` ${paint(optionName, "yellow")}`;
|
|
342
|
+
const type = getBaseType(f) ?? "string";
|
|
343
|
+
const valueLabel = `<${type}>`;
|
|
344
|
+
const value = isBooleanOption(f) ? "" : ` ${paint(valueLabel, "gray")}`;
|
|
345
|
+
const requiredLabel = isOptional(f) ? "" : paint(" (required)", "gray");
|
|
346
|
+
const desc = getDescription(f);
|
|
347
|
+
const description = desc ? ` ${desc}` : "";
|
|
348
|
+
return ` ${names}${value}${requiredLabel}${description}`;
|
|
349
|
+
});
|
|
350
|
+
};
|
|
351
|
+
var getUsageLine = (node, commandPath, binName, paint) => {
|
|
352
|
+
const usageParts = [paint([binName, ...commandPath].join(" "), "cyan", "bold")];
|
|
353
|
+
if (getCommandChildren(node).size > 0) usageParts.push("<command>");
|
|
354
|
+
if (node.def.options && getShapeKeys(node.def.options).length > 0) usageParts.push("[options]");
|
|
355
|
+
const args = getArgumentUsage(node.def.args);
|
|
356
|
+
if (args) usageParts.push(args);
|
|
357
|
+
return `${paint("Usage:", "bold")} ${usageParts.join(" ")}`;
|
|
358
|
+
};
|
|
359
|
+
var pushArgumentLines = (lines, node, paint) => {
|
|
360
|
+
const argKeys = getShapeKeys(node.def.args);
|
|
361
|
+
if (argKeys.length === 0) return;
|
|
362
|
+
lines.push("", paint("Arguments:", "bold"));
|
|
363
|
+
for (const k of argKeys) {
|
|
364
|
+
const f = getField(node.def.args, k);
|
|
365
|
+
const d = getDescription(f);
|
|
366
|
+
const typeLabel = `<${getBaseType(f) ?? "string"}>`;
|
|
367
|
+
const description = d ? ` ${d}` : "";
|
|
368
|
+
lines.push(` ${paint(k, "cyan")} ${paint(typeLabel, "gray")}${description}`);
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
var pushOptionLines = (lines, node, paint) => {
|
|
372
|
+
if (!node.def.options || getShapeKeys(node.def.options).length === 0) return;
|
|
373
|
+
lines.push("", paint("Options:", "bold"));
|
|
374
|
+
lines.push(...getOptionLines(node.def.options, paint));
|
|
375
|
+
};
|
|
376
|
+
var pushCommandLines = (lines, node, paint) => {
|
|
377
|
+
const children = getCommandChildren(node);
|
|
378
|
+
if (children.size === 0) return;
|
|
379
|
+
lines.push("", paint("Commands:", "bold"));
|
|
380
|
+
for (const [name, child] of children) {
|
|
381
|
+
if (child.def.hidden) continue;
|
|
382
|
+
const aliases = getCommandAliases(child.def);
|
|
383
|
+
const label = aliases.length > 0 ? `${name} (${aliases.join(", ")})` : name;
|
|
384
|
+
const description = child.def.description ? ` ${child.def.description}` : "";
|
|
385
|
+
lines.push(` ${paint(label, "cyan")}${description}`);
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
var pushExampleLines = (lines, node, paint) => {
|
|
389
|
+
if (!node.def.examples || node.def.examples.length === 0) return;
|
|
390
|
+
lines.push("", paint("Examples:", "bold"));
|
|
391
|
+
for (const ex of node.def.examples) {
|
|
392
|
+
if (ex.description) lines.push(` ${paint("#", "gray")} ${ex.description}`);
|
|
393
|
+
lines.push(` ${ex.command}`);
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
var getGlobalLabel = (g) => {
|
|
397
|
+
if (g.name === "color") return "--color / --no-color";
|
|
398
|
+
const value = g.bool ? "" : " <value>";
|
|
399
|
+
return `--${toKebabCase(g.name)}${value}`;
|
|
400
|
+
};
|
|
401
|
+
var renderHelp = (node, commandPath, binName, paint, globals = GLOBAL_FLAGS) => {
|
|
402
|
+
const lines = [getUsageLine(node, commandPath, binName, paint)];
|
|
403
|
+
if (node.def.description) lines.push("", node.def.description);
|
|
404
|
+
pushArgumentLines(lines, node, paint);
|
|
405
|
+
pushOptionLines(lines, node, paint);
|
|
406
|
+
pushCommandLines(lines, node, paint);
|
|
407
|
+
pushExampleLines(lines, node, paint);
|
|
408
|
+
lines.push("", paint("Global options:", "bold"));
|
|
409
|
+
for (const g of globals) lines.push(` ${paint(getGlobalLabel(g), "yellow")} ${g.description}`);
|
|
410
|
+
return `${lines.join("\n")}
|
|
411
|
+
`;
|
|
412
|
+
};
|
|
413
|
+
var getFieldDescriptions = (obj, prefix) => {
|
|
414
|
+
return getShapeKeys(obj).map((k) => {
|
|
415
|
+
const f = getField(obj, k);
|
|
416
|
+
const req = isOptional(f) ? "optional" : "required";
|
|
417
|
+
const d = getDescription(f);
|
|
418
|
+
const description = d ? `: ${d}` : "";
|
|
419
|
+
return `- ${prefix} \`${k}\` (${getBaseType(f) ?? "string"}, ${req})${description}`;
|
|
420
|
+
});
|
|
421
|
+
};
|
|
422
|
+
var getOutputDescriptions = (schema) => {
|
|
423
|
+
if (!schema) return [];
|
|
424
|
+
if (getBaseType(schema) !== "object") return [`- output (${getBaseType(schema) ?? "unknown"}, required)`];
|
|
425
|
+
return getFieldDescriptions(schema, "output");
|
|
426
|
+
};
|
|
427
|
+
var renderLlms = (binName, entries) => {
|
|
428
|
+
const lines = [`# ${binName}`, ""];
|
|
429
|
+
for (const e of entries) {
|
|
430
|
+
if (e.def.hidden) continue;
|
|
431
|
+
const name = [binName, ...e.commandPath].join(" ");
|
|
432
|
+
lines.push(`## ${name}`);
|
|
433
|
+
if (e.def.description) lines.push(e.def.description);
|
|
434
|
+
const args = getFieldDescriptions(e.def.args, "arg");
|
|
435
|
+
const opts = getFieldDescriptions(e.def.options, "option");
|
|
436
|
+
const env = getFieldDescriptions(e.def.env, "env");
|
|
437
|
+
const output = getOutputDescriptions(e.def.outputSchema);
|
|
438
|
+
lines.push(...args, ...opts, ...env, ...output, "");
|
|
439
|
+
}
|
|
440
|
+
return `${lines.join("\n").trimEnd()}
|
|
441
|
+
`;
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
// src/render/result.ts
|
|
445
|
+
var createSuccessResult = (data) => {
|
|
446
|
+
return {
|
|
447
|
+
ok: true,
|
|
448
|
+
data: data === void 0 ? null : data
|
|
449
|
+
};
|
|
450
|
+
};
|
|
451
|
+
var createErrorResult = (code, message) => {
|
|
452
|
+
return { ok: false, error: { code, message } };
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
// src/render/suggest.ts
|
|
456
|
+
var getLevenshteinDistance = (a, b) => {
|
|
457
|
+
const m = a.length;
|
|
458
|
+
const n = b.length;
|
|
459
|
+
if (m === 0) return n;
|
|
460
|
+
if (n === 0) return m;
|
|
461
|
+
const row = Array.from({ length: n + 1 });
|
|
462
|
+
for (let j = 0; j <= n; j++) row[j] = j;
|
|
463
|
+
for (let i = 1; i <= m; i++) {
|
|
464
|
+
let prev = row[0];
|
|
465
|
+
row[0] = i;
|
|
466
|
+
for (let j = 1; j <= n; j++) {
|
|
467
|
+
const tmp = row[j];
|
|
468
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
469
|
+
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + cost);
|
|
470
|
+
prev = tmp;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return row[n];
|
|
474
|
+
};
|
|
475
|
+
var suggest = (input, candidates, max = 2) => {
|
|
476
|
+
return candidates.map((c) => ({ c, d: getLevenshteinDistance(input, c) })).filter((x) => x.d >= 1 && x.d <= max).sort((a, b) => a.d - b.d || a.c.localeCompare(b.c)).map((x) => x.c);
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
// src/schemas/input-model.ts
|
|
480
|
+
var buildInputModel = (def) => {
|
|
481
|
+
return {
|
|
482
|
+
args: def.args,
|
|
483
|
+
options: def.options,
|
|
484
|
+
env: def.env,
|
|
485
|
+
argKeys: getShapeKeys(def.args),
|
|
486
|
+
optionKeys: getShapeKeys(def.options),
|
|
487
|
+
envKeys: getShapeKeys(def.env)
|
|
488
|
+
};
|
|
489
|
+
};
|
|
490
|
+
var getInputSchemas = (model, parts) => {
|
|
491
|
+
return parts.map((part) => model[part]);
|
|
492
|
+
};
|
|
493
|
+
var assertUniqueInputKeys = (model, parts) => {
|
|
494
|
+
const conflicts = getDuplicateKeys(getInputSchemas(model, parts));
|
|
495
|
+
if (conflicts.length > 0) throw new PicocliError("VALIDATION", getDuplicateKeyMessage(conflicts));
|
|
496
|
+
};
|
|
497
|
+
var getInputJsonSchema = (model, parts) => {
|
|
498
|
+
return toJsonSchema(mergeObjects(getInputSchemas(model, parts)));
|
|
499
|
+
};
|
|
500
|
+
var getEnvInputForKeys = (envKeys, processEnv) => {
|
|
501
|
+
const input = {};
|
|
502
|
+
for (const key of envKeys) {
|
|
503
|
+
const value = processEnv[key];
|
|
504
|
+
if (value !== void 0) input[key] = value;
|
|
505
|
+
}
|
|
506
|
+
return input;
|
|
507
|
+
};
|
|
508
|
+
var getSchemaEnvInput = (env, processEnv) => {
|
|
509
|
+
return getEnvInputForKeys(getShapeKeys(env), processEnv);
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
// src/command/resolve.ts
|
|
513
|
+
var resolveCommandPath = (root, argv, getShouldConsumeValue) => {
|
|
514
|
+
let node = root;
|
|
515
|
+
const commandPath = [];
|
|
516
|
+
const remainingArgv = [];
|
|
517
|
+
for (let i = 0; i < argv.length; i++) {
|
|
518
|
+
const token = argv[i];
|
|
519
|
+
if (token.startsWith("--") && token.length < 3) {
|
|
520
|
+
remainingArgv.push(...argv.slice(i));
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
if (isLongFlagToken(token)) {
|
|
524
|
+
remainingArgv.push(token);
|
|
525
|
+
const shouldConsumeValue = getShouldConsumeValue?.(token, commandPath) ?? false;
|
|
526
|
+
if (shouldConsumeValue && !token.includes("=")) {
|
|
527
|
+
const value = argv[i + 1];
|
|
528
|
+
if (value !== void 0) {
|
|
529
|
+
remainingArgv.push(value);
|
|
530
|
+
i++;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
const child = findCommandChild(node, token);
|
|
536
|
+
if (child) {
|
|
537
|
+
const [name, next] = child;
|
|
538
|
+
node = next;
|
|
539
|
+
commandPath.push(name);
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
remainingArgv.push(...argv.slice(i));
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
return { node, commandPath, remainingArgv };
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
// src/flags/model.ts
|
|
549
|
+
var toCamelCase = (s) => s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
550
|
+
var buildFlagModel = (options, globals) => {
|
|
551
|
+
const optKeys = getShapeKeys(options);
|
|
552
|
+
const known = new Set(optKeys);
|
|
553
|
+
const longMap = /* @__PURE__ */ new Map();
|
|
554
|
+
const boolNames = /* @__PURE__ */ new Set();
|
|
555
|
+
for (const key of optKeys) {
|
|
556
|
+
longMap.set(key, key);
|
|
557
|
+
longMap.set(toKebabCase(key), key);
|
|
558
|
+
if (isBooleanOption(getField(options, key))) boolNames.add(key);
|
|
559
|
+
}
|
|
560
|
+
for (const flag of globals) {
|
|
561
|
+
if (known.has(flag.name)) continue;
|
|
562
|
+
longMap.set(flag.name, flag.name);
|
|
563
|
+
longMap.set(toKebabCase(flag.name), flag.name);
|
|
564
|
+
if (flag.bool) boolNames.add(flag.name);
|
|
565
|
+
}
|
|
566
|
+
return {
|
|
567
|
+
long: (raw) => longMap.get(raw) ?? (known.has(toCamelCase(raw)) ? toCamelCase(raw) : void 0),
|
|
568
|
+
negatable: (no) => {
|
|
569
|
+
const raw = no.slice(3);
|
|
570
|
+
const name = longMap.get(raw) ?? (known.has(toCamelCase(raw)) ? toCamelCase(raw) : void 0);
|
|
571
|
+
return name && boolNames.has(name) ? name : void 0;
|
|
572
|
+
},
|
|
573
|
+
isBoolean: (name) => boolNames.has(name)
|
|
574
|
+
};
|
|
575
|
+
};
|
|
576
|
+
var getShouldConsumeFlagValue = (options) => {
|
|
577
|
+
const optionKeys = new Set(getShapeKeys(options));
|
|
578
|
+
const model = buildFlagModel(options, GLOBAL_FLAGS);
|
|
579
|
+
const valueGlobals = new Set(GLOBAL_FLAGS.filter((flag) => !flag.bool).map((flag) => flag.name));
|
|
580
|
+
return (flagToken) => {
|
|
581
|
+
if (flagToken.startsWith("--")) {
|
|
582
|
+
let raw = flagToken.slice(2);
|
|
583
|
+
if (raw.includes("=")) return false;
|
|
584
|
+
if (raw.startsWith("no-")) raw = raw.slice(3);
|
|
585
|
+
const name = model.long(raw);
|
|
586
|
+
if (!name) return false;
|
|
587
|
+
if (optionKeys.has(name)) return !model.isBoolean(name);
|
|
588
|
+
return valueGlobals.has(name);
|
|
589
|
+
}
|
|
590
|
+
return false;
|
|
591
|
+
};
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
// src/render/color.ts
|
|
595
|
+
var getShouldUseAnsi = (params) => {
|
|
596
|
+
if (params.colorFlag === false) return false;
|
|
597
|
+
if (params.colorFlag === true) return true;
|
|
598
|
+
const fc = params.env.FORCE_COLOR;
|
|
599
|
+
if (fc !== void 0) return !(fc === "0" || fc === "false" || fc === "");
|
|
600
|
+
if (params.env.NO_COLOR !== void 0 && params.env.NO_COLOR !== "") return false;
|
|
601
|
+
return params.isTTY;
|
|
602
|
+
};
|
|
603
|
+
var CODES = {
|
|
604
|
+
bold: 1,
|
|
605
|
+
red: 31,
|
|
606
|
+
green: 32,
|
|
607
|
+
yellow: 33,
|
|
608
|
+
cyan: 36,
|
|
609
|
+
gray: 90
|
|
610
|
+
};
|
|
611
|
+
var makePaint = (shouldUseAnsi) => {
|
|
612
|
+
return (s, ...attributes) => {
|
|
613
|
+
if (!shouldUseAnsi || attributes.length === 0) return s;
|
|
614
|
+
const open = attributes.map((attribute) => `\x1B[${CODES[attribute]}m`).join("");
|
|
615
|
+
return `${open}${s}\x1B[0m`;
|
|
616
|
+
};
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
// src/runtime/argv.ts
|
|
620
|
+
var resolveInvocation = (root, argv) => {
|
|
621
|
+
const treeIssue = findCommandTreeIssue(root);
|
|
622
|
+
if (treeIssue) throw new PicocliError("VALIDATION", treeIssue);
|
|
623
|
+
const { node, commandPath, remainingArgv } = resolveCommandPath(root, argv, (flagToken, path) => {
|
|
624
|
+
const def2 = getEffectiveCommandDefinition(getCommandChain(root, [...path]));
|
|
625
|
+
return getShouldConsumeFlagValue(def2.options)(flagToken);
|
|
626
|
+
});
|
|
627
|
+
const chain = getCommandChain(root, commandPath);
|
|
628
|
+
const def = getEffectiveCommandDefinition(chain);
|
|
629
|
+
const input = buildInputModel(def);
|
|
630
|
+
return {
|
|
631
|
+
node,
|
|
632
|
+
helpNode: createCommandNodeView(node, def),
|
|
633
|
+
commandPath,
|
|
634
|
+
remainingArgv,
|
|
635
|
+
commandName: [root.name, ...commandPath].join(" "),
|
|
636
|
+
hasSubcommands: getCommandChildren(node).size > 0,
|
|
637
|
+
def,
|
|
638
|
+
input
|
|
639
|
+
};
|
|
640
|
+
};
|
|
641
|
+
var parseGlobals = (params) => {
|
|
642
|
+
const { input, remainingArgv, opts, env, isTTY, features } = params;
|
|
643
|
+
const optionKeys = input.optionKeys;
|
|
644
|
+
const globals = getActiveGlobalFlags(optionKeys, features);
|
|
645
|
+
const globalNames = new Set(globals.map((flag) => flag.name));
|
|
646
|
+
const tokens = tokenizeArgv(remainingArgv, buildFlagModel(input.options, globals));
|
|
647
|
+
const lastValue = (name) => {
|
|
648
|
+
const values = tokens.flags.get(name);
|
|
649
|
+
return values ? values[values.length - 1] : void 0;
|
|
650
|
+
};
|
|
651
|
+
const has = (name) => globalNames.has(name) && tokens.flags.has(name) && lastValue(name) !== false;
|
|
652
|
+
const colorFlag = globalNames.has("color") && tokens.flags.has("color") ? lastValue("color") === true : void 0;
|
|
653
|
+
const fmtVal = has("format") ? lastValue("format") : void 0;
|
|
654
|
+
return {
|
|
655
|
+
tokens,
|
|
656
|
+
optionKeys,
|
|
657
|
+
globals,
|
|
658
|
+
globalNames,
|
|
659
|
+
paint: makePaint(getShouldUseAnsi({ colorFlag, env, isTTY })),
|
|
660
|
+
isJSON: opts.format === "json" || opts.format !== "pretty" && (has("json") || fmtVal === "json"),
|
|
661
|
+
has,
|
|
662
|
+
lastValue
|
|
663
|
+
};
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
// src/runtime/input.ts
|
|
667
|
+
var collectOptionInput = (options, flags) => {
|
|
668
|
+
const optionsInput = {};
|
|
669
|
+
for (const key of getShapeKeys(options)) {
|
|
670
|
+
const flagValues = flags.get(key);
|
|
671
|
+
if (flagValues && flagValues.length > 0) {
|
|
672
|
+
optionsInput[key] = isArrayOption(getField(options, key)) ? flagValues : flagValues[flagValues.length - 1];
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return optionsInput;
|
|
676
|
+
};
|
|
677
|
+
var collectArgsInput = (args, positionals) => {
|
|
678
|
+
const argKeys = getShapeKeys(args);
|
|
679
|
+
const lastIndex = argKeys.length - 1;
|
|
680
|
+
const lastArgKey = argKeys.at(-1);
|
|
681
|
+
const lastIsArray = lastArgKey !== void 0 && isArrayOption(getField(args, lastArgKey));
|
|
682
|
+
const argsInput = {};
|
|
683
|
+
let excessPositionals = [];
|
|
684
|
+
for (const [index, argKey] of argKeys.entries()) {
|
|
685
|
+
if (index === lastIndex && lastIsArray) {
|
|
686
|
+
const collected = positionals.slice(index);
|
|
687
|
+
if (collected.length > 0) argsInput[argKey] = collected;
|
|
688
|
+
} else if (positionals[index] !== void 0) {
|
|
689
|
+
argsInput[argKey] = positionals[index];
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
if (!lastIsArray && positionals.length > argKeys.length) {
|
|
693
|
+
excessPositionals = positionals.slice(argKeys.length);
|
|
694
|
+
}
|
|
695
|
+
return { argsInput, excessPositionals };
|
|
696
|
+
};
|
|
697
|
+
var parseCommandInput = (params) => {
|
|
698
|
+
const { args, options, env, flags, positionals, processEnv } = params;
|
|
699
|
+
const optionsInput = collectOptionInput(options, flags);
|
|
700
|
+
const { argsInput, excessPositionals } = collectArgsInput(args, positionals);
|
|
701
|
+
const envInput = getSchemaEnvInput(env, processEnv);
|
|
702
|
+
return { argsInput, optionsInput, envInput, excessPositionals };
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
// src/schemas/validate.ts
|
|
706
|
+
var parseSchema = (schema, input, kind) => {
|
|
707
|
+
const result = schema.safeParse(input);
|
|
708
|
+
if (result.success) return result.data;
|
|
709
|
+
const issue = result.error.issues[0] ?? {};
|
|
710
|
+
const field = issue.path?.join(".") || "(input)";
|
|
711
|
+
const expected = "expected" in issue ? String(issue.expected) : void 0;
|
|
712
|
+
const coercible = expected === "number" || expected === "int" || expected === "bigint";
|
|
713
|
+
const hint = coercible ? ` (CLI values arrive as strings \u2014 use z.coerce.${expected}() for non-string ${kind}s)` : "";
|
|
714
|
+
const message = `invalid ${kind} "${field}": ${issue.message}${hint}`;
|
|
715
|
+
throw new PicocliError("VALIDATION", message);
|
|
716
|
+
};
|
|
717
|
+
var parseOutputSchema = (schema, data) => {
|
|
718
|
+
const result = schema.safeParse(data);
|
|
719
|
+
if (result.success) return result.data;
|
|
720
|
+
const issue = result.error.issues[0] ?? {};
|
|
721
|
+
const field = issue.path?.join(".") || "(output)";
|
|
722
|
+
const message = `invalid output "${field}": ${issue.message}`;
|
|
723
|
+
throw new PicocliError("VALIDATION", message);
|
|
724
|
+
};
|
|
725
|
+
var validateAll = (params) => {
|
|
726
|
+
const { args, options, env, argsInput, optionsInput, envInput } = params;
|
|
727
|
+
return {
|
|
728
|
+
args: args ? parseSchema(args, argsInput, "argument") : {},
|
|
729
|
+
options: options ? parseSchema(options, optionsInput, "option") : {},
|
|
730
|
+
env: env ? parseSchema(env, envInput, "environment variable") : {}
|
|
731
|
+
};
|
|
732
|
+
};
|
|
733
|
+
var METHODS = ["log", "info", "debug", "warn", "error"];
|
|
734
|
+
var consoleCaptureStorage = new async_hooks.AsyncLocalStorage();
|
|
735
|
+
var depth = 0;
|
|
736
|
+
var original;
|
|
737
|
+
var createConsoleRouter = (method) => (...args) => {
|
|
738
|
+
const capture = consoleCaptureStorage.getStore();
|
|
739
|
+
const handler = capture?.[method];
|
|
740
|
+
if (handler) handler(args);
|
|
741
|
+
else original?.[method](...args);
|
|
742
|
+
};
|
|
743
|
+
var installConsoleCapture = () => {
|
|
744
|
+
if (depth++ > 0) return;
|
|
745
|
+
original = Object.fromEntries(METHODS.map((method) => [method, console[method].bind(console)]));
|
|
746
|
+
for (const method of METHODS) {
|
|
747
|
+
console[method] = createConsoleRouter(method);
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
var uninstallConsoleCapture = () => {
|
|
751
|
+
depth--;
|
|
752
|
+
if (depth > 0 || !original) return;
|
|
753
|
+
for (const method of METHODS) console[method] = original[method];
|
|
754
|
+
original = void 0;
|
|
755
|
+
};
|
|
756
|
+
var withConsoleCapture = async (capture, runCaptured) => {
|
|
757
|
+
installConsoleCapture();
|
|
758
|
+
try {
|
|
759
|
+
return await consoleCaptureStorage.run(capture, runCaptured);
|
|
760
|
+
} finally {
|
|
761
|
+
uninstallConsoleCapture();
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
var suppressedConsole = Object.fromEntries(
|
|
765
|
+
METHODS.map((method) => [method, () => {
|
|
766
|
+
}])
|
|
767
|
+
);
|
|
768
|
+
|
|
769
|
+
// src/runtime/run.ts
|
|
770
|
+
var shouldValidateOutput = (mode, nodeEnv) => {
|
|
771
|
+
const resolved = mode ?? "development";
|
|
772
|
+
if (resolved === true) return true;
|
|
773
|
+
if (resolved === false) return false;
|
|
774
|
+
return nodeEnv === "development";
|
|
775
|
+
};
|
|
776
|
+
var createUnknownErrorResult = (error, debug) => {
|
|
777
|
+
debug?.(`${error.stack ?? String(error)}
|
|
778
|
+
`);
|
|
779
|
+
return {
|
|
780
|
+
ok: false,
|
|
781
|
+
error: { code: "UNKNOWN", message: error.message ?? String(error) },
|
|
782
|
+
exitCode: 1
|
|
783
|
+
};
|
|
784
|
+
};
|
|
785
|
+
var createThrownErrorResult = (error, debug) => {
|
|
786
|
+
if (isPicocliError(error)) {
|
|
787
|
+
return {
|
|
788
|
+
ok: false,
|
|
789
|
+
error: { code: error.code, message: error.message },
|
|
790
|
+
exitCode: getExitCodeForError(error.code)
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
return createUnknownErrorResult(error, debug);
|
|
794
|
+
};
|
|
795
|
+
var runCommand = async (def, baseContext, opts = {}) => {
|
|
796
|
+
const run = async () => {
|
|
797
|
+
try {
|
|
798
|
+
const retval = await def.run(baseContext);
|
|
799
|
+
const returnedData = retval === void 0 ? null : retval;
|
|
800
|
+
const data = def.outputSchema && shouldValidateOutput(def.validateOutput, opts.nodeEnv) ? parseOutputSchema(def.outputSchema, returnedData) : returnedData;
|
|
801
|
+
return { ok: true, data };
|
|
802
|
+
} catch (error) {
|
|
803
|
+
return createThrownErrorResult(error, opts.debug);
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
return opts.suppressConsole ? withConsoleCapture(suppressedConsole, run) : run();
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
// src/runtime/invoke.ts
|
|
810
|
+
var validateCommandInput = (input, values) => {
|
|
811
|
+
try {
|
|
812
|
+
return validateAll({
|
|
813
|
+
args: input.args,
|
|
814
|
+
options: input.options,
|
|
815
|
+
env: input.env,
|
|
816
|
+
argsInput: values.argsInput,
|
|
817
|
+
optionsInput: values.optionsInput,
|
|
818
|
+
envInput: values.envInput
|
|
819
|
+
});
|
|
820
|
+
} catch (error) {
|
|
821
|
+
if (!isPicocliError(error)) throw error;
|
|
822
|
+
return error;
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
var createValidationResult = (error) => ({
|
|
826
|
+
ok: false,
|
|
827
|
+
result: createErrorResult(error.code, error.message),
|
|
828
|
+
error: { code: error.code, message: error.message },
|
|
829
|
+
exitCode: getExitCodeForError(error.code)
|
|
830
|
+
});
|
|
831
|
+
var createInvocationResult = (result) => {
|
|
832
|
+
if (result.ok) {
|
|
833
|
+
return {
|
|
834
|
+
ok: true,
|
|
835
|
+
result: createSuccessResult(result.data),
|
|
836
|
+
exitCode: 0
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
return {
|
|
840
|
+
ok: false,
|
|
841
|
+
result: createErrorResult(result.error.code, result.error.message),
|
|
842
|
+
error: result.error,
|
|
843
|
+
exitCode: result.exitCode
|
|
844
|
+
};
|
|
845
|
+
};
|
|
846
|
+
var invokeCommand = async (params) => {
|
|
847
|
+
const { def, input, name, inputs, isTTY, isJSON, rest, readStdin, suppressConsole, debug, nodeEnv } = params;
|
|
848
|
+
const parsed = validateCommandInput(input, inputs);
|
|
849
|
+
if (isPicocliError(parsed)) return createValidationResult(parsed);
|
|
850
|
+
const result = await runCommand(
|
|
851
|
+
def,
|
|
852
|
+
{
|
|
853
|
+
name,
|
|
854
|
+
args: parsed.args,
|
|
855
|
+
options: parsed.options,
|
|
856
|
+
env: parsed.env,
|
|
857
|
+
isTTY,
|
|
858
|
+
isJSON,
|
|
859
|
+
rest,
|
|
860
|
+
readStdin
|
|
861
|
+
},
|
|
862
|
+
{ suppressConsole, debug, nodeEnv }
|
|
863
|
+
);
|
|
864
|
+
return createInvocationResult(result);
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
// src/runtime/serve.ts
|
|
868
|
+
var loadMcp = async () => {
|
|
869
|
+
const distSpecifier = "./mcp/index.js";
|
|
870
|
+
try {
|
|
871
|
+
return await import(distSpecifier);
|
|
872
|
+
} catch (error) {
|
|
873
|
+
const sourceSpecifier = "../mcp/index";
|
|
874
|
+
try {
|
|
875
|
+
return await import(sourceSpecifier);
|
|
876
|
+
} catch {
|
|
877
|
+
throw error;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
var readProcessStdin = async () => {
|
|
882
|
+
const stdin = process.stdin;
|
|
883
|
+
if (stdin.isTTY) return "";
|
|
884
|
+
const chunks = [];
|
|
885
|
+
for await (const chunk of stdin) chunks.push(chunk);
|
|
886
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
887
|
+
};
|
|
888
|
+
var createServeContext = (root, argv, opts) => {
|
|
889
|
+
const stdout = opts.stdout ?? ((s) => void process.stdout.write(s));
|
|
890
|
+
const stderr = opts.stderr ?? ((s) => void process.stderr.write(s));
|
|
891
|
+
const exit = opts.exit ?? ((c) => process.exit(c));
|
|
892
|
+
const env = opts.env ?? process.env;
|
|
893
|
+
const isTTY = opts.isTTY ?? Boolean(process.stdout.isTTY);
|
|
894
|
+
const invocation = resolveInvocation(root, argv);
|
|
895
|
+
const parsedGlobals = parseGlobals({
|
|
896
|
+
input: invocation.input,
|
|
897
|
+
remainingArgv: invocation.remainingArgv,
|
|
898
|
+
opts,
|
|
899
|
+
env,
|
|
900
|
+
isTTY,
|
|
901
|
+
features: root.features
|
|
902
|
+
});
|
|
903
|
+
return { root, opts, stdout, stderr, exit, env, isTTY, invocation, parsedGlobals };
|
|
904
|
+
};
|
|
905
|
+
var writeJsonLine = (write, value) => {
|
|
906
|
+
write(`${JSON.stringify(value)}
|
|
907
|
+
`);
|
|
908
|
+
};
|
|
909
|
+
var writeFailure = (ctx, error) => {
|
|
910
|
+
if (ctx.parsedGlobals.isJSON) writeJsonLine(ctx.stdout, createErrorResult(error.code, error.message));
|
|
911
|
+
else ctx.stderr(renderError(error.code, error.message, ctx.parsedGlobals.paint));
|
|
912
|
+
return ctx.exit(getExitCodeForError(error.code));
|
|
913
|
+
};
|
|
914
|
+
var finishCommand = (ctx, result) => {
|
|
915
|
+
if (ctx.parsedGlobals.isJSON) writeJsonLine(ctx.stdout, result.ok ? result.result.data : result.result);
|
|
916
|
+
else if (!result.ok) {
|
|
917
|
+
ctx.stderr(renderError(result.error.code, result.error.message, ctx.parsedGlobals.paint));
|
|
918
|
+
}
|
|
919
|
+
return ctx.exit(result.exitCode);
|
|
920
|
+
};
|
|
921
|
+
var writeHelp = (ctx) => {
|
|
922
|
+
ctx.stdout(
|
|
923
|
+
renderHelp(
|
|
924
|
+
ctx.invocation.helpNode,
|
|
925
|
+
ctx.invocation.commandPath,
|
|
926
|
+
ctx.root.name,
|
|
927
|
+
ctx.parsedGlobals.paint,
|
|
928
|
+
ctx.parsedGlobals.globals
|
|
929
|
+
)
|
|
930
|
+
);
|
|
931
|
+
};
|
|
932
|
+
var handleHelpOrVersion = async (ctx) => {
|
|
933
|
+
if (ctx.parsedGlobals.has("help")) {
|
|
934
|
+
writeHelp(ctx);
|
|
935
|
+
ctx.exit(0);
|
|
936
|
+
return true;
|
|
937
|
+
}
|
|
938
|
+
if (ctx.parsedGlobals.has("version")) {
|
|
939
|
+
ctx.stdout(`${ctx.invocation.def.version ?? ctx.root.def.version ?? "0.0.0"}
|
|
940
|
+
`);
|
|
941
|
+
ctx.exit(0);
|
|
942
|
+
return true;
|
|
943
|
+
}
|
|
944
|
+
return false;
|
|
945
|
+
};
|
|
946
|
+
var handleGlobalParseError = (ctx) => {
|
|
947
|
+
const { parsedGlobals } = ctx;
|
|
948
|
+
if (parsedGlobals.tokens.missing.length > 0) {
|
|
949
|
+
writeFailure(
|
|
950
|
+
ctx,
|
|
951
|
+
new PicocliError(
|
|
952
|
+
"VALIDATION",
|
|
953
|
+
`option "--${toKebabCase(parsedGlobals.tokens.missing[0])}" requires a value`
|
|
954
|
+
)
|
|
955
|
+
);
|
|
956
|
+
return true;
|
|
957
|
+
}
|
|
958
|
+
if (parsedGlobals.has("format")) {
|
|
959
|
+
const value = parsedGlobals.lastValue("format");
|
|
960
|
+
if (value !== "json" && value !== "pretty") {
|
|
961
|
+
const sugg = suggest(String(value), ["pretty", "json"]);
|
|
962
|
+
const hint = sugg.length > 0 ? `; did you mean "${sugg[0]}"?` : "";
|
|
963
|
+
writeFailure(
|
|
964
|
+
ctx,
|
|
965
|
+
new PicocliError("VALIDATION", `invalid --format "${value}"; expected pretty or json${hint}`)
|
|
966
|
+
);
|
|
967
|
+
return true;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
return false;
|
|
971
|
+
};
|
|
972
|
+
var handleSchemaManifestOrMcp = async (ctx) => {
|
|
973
|
+
const { env, invocation, parsedGlobals, root } = ctx;
|
|
974
|
+
if (parsedGlobals.has("schema")) {
|
|
975
|
+
try {
|
|
976
|
+
assertUniqueInputKeys(invocation.input, ["args", "options"]);
|
|
977
|
+
} catch (error) {
|
|
978
|
+
if (isPicocliError(error)) writeFailure(ctx, error);
|
|
979
|
+
else throw error;
|
|
980
|
+
return true;
|
|
981
|
+
}
|
|
982
|
+
ctx.stdout(
|
|
983
|
+
`${JSON.stringify(
|
|
984
|
+
{
|
|
985
|
+
input: getInputJsonSchema(invocation.input, ["args", "options"]),
|
|
986
|
+
output: invocation.def.outputSchema ? toJsonSchema(invocation.def.outputSchema) : null
|
|
987
|
+
},
|
|
988
|
+
null,
|
|
989
|
+
2
|
|
990
|
+
)}
|
|
991
|
+
`
|
|
992
|
+
);
|
|
993
|
+
ctx.exit(0);
|
|
994
|
+
return true;
|
|
995
|
+
}
|
|
996
|
+
if (parsedGlobals.has("llms")) {
|
|
997
|
+
ctx.stdout(renderLlms(root.name, collectCommandEntries(root)));
|
|
998
|
+
ctx.exit(0);
|
|
999
|
+
return true;
|
|
1000
|
+
}
|
|
1001
|
+
if (parsedGlobals.has("mcp")) {
|
|
1002
|
+
const mcp = await loadMcp();
|
|
1003
|
+
await mcp.serveMcp(root, { env });
|
|
1004
|
+
return true;
|
|
1005
|
+
}
|
|
1006
|
+
return false;
|
|
1007
|
+
};
|
|
1008
|
+
var handleUnknownOption = (ctx) => {
|
|
1009
|
+
const { parsedGlobals } = ctx;
|
|
1010
|
+
if (parsedGlobals.tokens.unknown.length === 0) return false;
|
|
1011
|
+
const bad = parsedGlobals.tokens.unknown[0];
|
|
1012
|
+
const known = [
|
|
1013
|
+
...parsedGlobals.optionKeys.map(toKebabCase),
|
|
1014
|
+
...parsedGlobals.globals.map((flag) => flag.name)
|
|
1015
|
+
];
|
|
1016
|
+
const sugg = bad.startsWith("--") ? suggest(bad.slice(2), known) : [];
|
|
1017
|
+
const hint = sugg.length > 0 ? `; did you mean "--${sugg[0]}"?` : "";
|
|
1018
|
+
writeFailure(ctx, new PicocliError("VALIDATION", `unknown option "${bad}"${hint}`));
|
|
1019
|
+
return true;
|
|
1020
|
+
};
|
|
1021
|
+
var handleNonRunnableCommand = (ctx) => {
|
|
1022
|
+
const { invocation, parsedGlobals } = ctx;
|
|
1023
|
+
if (invocation.hasSubcommands && !invocation.def.run) {
|
|
1024
|
+
if (parsedGlobals.tokens.positionals.length > 0) {
|
|
1025
|
+
const first = parsedGlobals.tokens.positionals[0];
|
|
1026
|
+
const sugg = suggest(first, getChildNames(invocation.node));
|
|
1027
|
+
const hint = sugg.length > 0 ? `; did you mean "${sugg[0]}"?` : "";
|
|
1028
|
+
writeFailure(ctx, new PicocliError("COMMAND_NOT_FOUND", `unknown command "${first}"${hint}`));
|
|
1029
|
+
return true;
|
|
1030
|
+
}
|
|
1031
|
+
writeHelp(ctx);
|
|
1032
|
+
ctx.exit(0);
|
|
1033
|
+
return true;
|
|
1034
|
+
}
|
|
1035
|
+
if (!invocation.def.run) {
|
|
1036
|
+
writeHelp(ctx);
|
|
1037
|
+
ctx.exit(0);
|
|
1038
|
+
return true;
|
|
1039
|
+
}
|
|
1040
|
+
return false;
|
|
1041
|
+
};
|
|
1042
|
+
var runCommandInvocation = async (ctx) => {
|
|
1043
|
+
const { env, invocation, isTTY, opts, parsedGlobals, stderr } = ctx;
|
|
1044
|
+
const cmdFlags = new Map(
|
|
1045
|
+
[...parsedGlobals.tokens.flags].filter(([name]) => !parsedGlobals.globalNames.has(name))
|
|
1046
|
+
);
|
|
1047
|
+
const commandInput = parseCommandInput({
|
|
1048
|
+
args: invocation.input.args,
|
|
1049
|
+
options: invocation.input.options,
|
|
1050
|
+
env: invocation.input.env,
|
|
1051
|
+
flags: cmdFlags,
|
|
1052
|
+
positionals: parsedGlobals.tokens.positionals,
|
|
1053
|
+
processEnv: env
|
|
1054
|
+
});
|
|
1055
|
+
if (commandInput.excessPositionals.length > 0) {
|
|
1056
|
+
return writeFailure(
|
|
1057
|
+
ctx,
|
|
1058
|
+
new PicocliError("VALIDATION", `unexpected argument "${commandInput.excessPositionals[0]}"`)
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
const result = await invokeCommand({
|
|
1062
|
+
def: invocation.def,
|
|
1063
|
+
input: invocation.input,
|
|
1064
|
+
name: invocation.commandName,
|
|
1065
|
+
inputs: {
|
|
1066
|
+
argsInput: commandInput.argsInput,
|
|
1067
|
+
optionsInput: commandInput.optionsInput,
|
|
1068
|
+
envInput: commandInput.envInput
|
|
1069
|
+
},
|
|
1070
|
+
isTTY,
|
|
1071
|
+
isJSON: parsedGlobals.isJSON,
|
|
1072
|
+
rest: parsedGlobals.tokens.rest,
|
|
1073
|
+
readStdin: opts.stdin ?? readProcessStdin,
|
|
1074
|
+
suppressConsole: parsedGlobals.isJSON,
|
|
1075
|
+
debug: env.DEBUG ? stderr : void 0,
|
|
1076
|
+
nodeEnv: env.NODE_ENV
|
|
1077
|
+
});
|
|
1078
|
+
return finishCommand(ctx, result);
|
|
1079
|
+
};
|
|
1080
|
+
var serve = async (root, argv, opts) => {
|
|
1081
|
+
const ctx = createServeContext(root, argv, opts);
|
|
1082
|
+
if (await handleHelpOrVersion(ctx)) return;
|
|
1083
|
+
if (handleGlobalParseError(ctx)) return;
|
|
1084
|
+
if (await handleSchemaManifestOrMcp(ctx)) return;
|
|
1085
|
+
if (handleUnknownOption(ctx)) return;
|
|
1086
|
+
if (handleNonRunnableCommand(ctx)) return;
|
|
1087
|
+
return runCommandInvocation(ctx);
|
|
1088
|
+
};
|
|
1089
|
+
|
|
1090
|
+
// src/create.ts
|
|
1091
|
+
var parentCliMap = /* @__PURE__ */ new WeakMap();
|
|
1092
|
+
var commandCliSet = /* @__PURE__ */ new WeakSet();
|
|
1093
|
+
var resolveCliFeatures = (features) => ({
|
|
1094
|
+
mcp: features?.mcp ?? true,
|
|
1095
|
+
schema: features?.schema ?? true,
|
|
1096
|
+
llms: features?.llms ?? true
|
|
1097
|
+
});
|
|
1098
|
+
var clearCachedTreeIssues = (cli2) => {
|
|
1099
|
+
clearCommandTreeIssueCache(cli2);
|
|
1100
|
+
for (const parent of parentCliMap.get(cli2) ?? []) clearCachedTreeIssues(parent);
|
|
1101
|
+
};
|
|
1102
|
+
var containsCommandNode = (root, target, seen = /* @__PURE__ */ new WeakSet()) => {
|
|
1103
|
+
if (root === target) return true;
|
|
1104
|
+
if (seen.has(root)) return false;
|
|
1105
|
+
seen.add(root);
|
|
1106
|
+
for (const [, child] of getCommandChildren(root)) {
|
|
1107
|
+
if (containsCommandNode(child, target, seen)) return true;
|
|
1108
|
+
}
|
|
1109
|
+
return false;
|
|
1110
|
+
};
|
|
1111
|
+
var Cli = class {
|
|
1112
|
+
name;
|
|
1113
|
+
def;
|
|
1114
|
+
features;
|
|
1115
|
+
constructor(name, def, features) {
|
|
1116
|
+
this.name = name;
|
|
1117
|
+
this.def = def ?? {};
|
|
1118
|
+
this.features = resolveCliFeatures(features);
|
|
1119
|
+
}
|
|
1120
|
+
command(a, b) {
|
|
1121
|
+
const name = typeof a === "string" ? a : a.name;
|
|
1122
|
+
if (getCommandChildren(this).has(name)) {
|
|
1123
|
+
throw new PicocliError("VALIDATION", `command "${name}" already exists under "${this.name}"`);
|
|
1124
|
+
}
|
|
1125
|
+
if (typeof a === "string") setCommandChild(this, name, { name, def: b ?? {} });
|
|
1126
|
+
else {
|
|
1127
|
+
if (!commandCliSet.has(a)) {
|
|
1128
|
+
throw new PicocliError("VALIDATION", `command "${name}" must be created with cli.command(...)`);
|
|
1129
|
+
}
|
|
1130
|
+
if (containsCommandNode(a, this)) {
|
|
1131
|
+
throw new PicocliError("VALIDATION", `command "${name}" cannot be mounted into its own tree`);
|
|
1132
|
+
}
|
|
1133
|
+
const parents = parentCliMap.get(a) ?? /* @__PURE__ */ new Set();
|
|
1134
|
+
parents.add(this);
|
|
1135
|
+
parentCliMap.set(a, parents);
|
|
1136
|
+
setCommandChild(this, name, a);
|
|
1137
|
+
}
|
|
1138
|
+
clearCachedTreeIssues(this);
|
|
1139
|
+
return this;
|
|
1140
|
+
}
|
|
1141
|
+
serve(argv, opts) {
|
|
1142
|
+
return serve(this, argv ?? process.argv.slice(2), opts ?? {});
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
var cli = {
|
|
1146
|
+
create: (name, def) => {
|
|
1147
|
+
const { features, ...commandDefinition } = def ?? {};
|
|
1148
|
+
return new Cli(
|
|
1149
|
+
name,
|
|
1150
|
+
commandDefinition,
|
|
1151
|
+
features
|
|
1152
|
+
);
|
|
1153
|
+
},
|
|
1154
|
+
command: (name, def) => {
|
|
1155
|
+
const command2 = new Cli(
|
|
1156
|
+
name,
|
|
1157
|
+
def
|
|
1158
|
+
);
|
|
1159
|
+
commandCliSet.add(command2);
|
|
1160
|
+
return command2;
|
|
1161
|
+
}
|
|
1162
|
+
};
|
|
1163
|
+
var create_default = cli;
|
|
1164
|
+
|
|
1165
|
+
// src/index.ts
|
|
1166
|
+
var src_default = create_default;
|
|
1167
|
+
var create = create_default.create;
|
|
1168
|
+
var command = create_default.command;
|
|
1169
|
+
|
|
1170
|
+
exports.Cli = Cli;
|
|
1171
|
+
exports.PicocliError = PicocliError;
|
|
1172
|
+
exports.command = command;
|
|
1173
|
+
exports.create = create;
|
|
1174
|
+
exports.default = src_default;
|
|
1175
|
+
exports.isPicocliError = isPicocliError;
|