@alcyone-labs/arg-parser 2.10.1 → 2.10.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/dist/index.mjs CHANGED
@@ -1370,6 +1370,22 @@ const _DxtPathResolver = class _DxtPathResolver {
1370
1370
  };
1371
1371
  _DxtPathResolver._cachedContext = null;
1372
1372
  let DxtPathResolver = _DxtPathResolver;
1373
+ const FlagInheritance = {
1374
+ /**
1375
+ * No flags are inherited from the parent.
1376
+ */
1377
+ NONE: "none",
1378
+ /**
1379
+ * Inherits flags only from the direct parent at the time of attachment (Snapshot behavior).
1380
+ * Equivalent to `true` in legacy boolean config.
1381
+ */
1382
+ DirectParentOnly: "direct-parent-only",
1383
+ /**
1384
+ * Inherits flags from the entire parent chain, ensuring grandchildren receive root flags
1385
+ * even in bottom-up construction scenarios.
1386
+ */
1387
+ AllParents: "all-parents"
1388
+ };
1373
1389
  const zodDxtOptionsSchema = z.object({
1374
1390
  sensitive: z.boolean().optional().describe(
1375
1391
  "Whether this field should be marked as sensitive in DXT user_config"
@@ -3685,6 +3701,7 @@ class ArgParserBase {
3685
3701
  #dxtGenerator;
3686
3702
  #configurationManager;
3687
3703
  #fuzzyMode = false;
3704
+ #triggerAutoHelpIfNoHandler = false;
3688
3705
  // Track dynamically added flags so we can clean them between parses
3689
3706
  #dynamicFlagNames = /* @__PURE__ */ new Set();
3690
3707
  // MCP-related managers
@@ -3712,6 +3729,7 @@ class ArgParserBase {
3712
3729
  this.#handleErrors = options.handleErrors ?? true;
3713
3730
  this.#autoExit = options.autoExit ?? true;
3714
3731
  this.#inheritParentFlags = options.inheritParentFlags ?? false;
3732
+ this.#triggerAutoHelpIfNoHandler = options.triggerAutoHelpIfNoHandler ?? false;
3715
3733
  this.#description = options.description;
3716
3734
  this.#handler = options.handler;
3717
3735
  this.#appCommandName = options.appCommandName;
@@ -3916,7 +3934,9 @@ class ArgParserBase {
3916
3934
  subParser.#appCommandName = this.#appCommandName;
3917
3935
  }
3918
3936
  subParser.#autoExit = this.#autoExit;
3919
- if (subParser.#inheritParentFlags) {
3937
+ subParser.#triggerAutoHelpIfNoHandler = this.#triggerAutoHelpIfNoHandler;
3938
+ const shouldInherit = subParser.#inheritParentFlags === true || subParser.#inheritParentFlags === FlagInheritance.DirectParentOnly || subParser.#inheritParentFlags === FlagInheritance.AllParents;
3939
+ if (shouldInherit) {
3920
3940
  const parentFlags = this.#flagManager.flags;
3921
3941
  for (const parentFlag of parentFlags) {
3922
3942
  if (!subParser.#flagManager.hasFlag(parentFlag["name"])) {
@@ -3925,11 +3945,36 @@ class ArgParserBase {
3925
3945
  }
3926
3946
  }
3927
3947
  this.#subCommands.set(subCommandConfig.name, subCommandConfig);
3948
+ this.#propagateFlagsRecursive(subParser);
3928
3949
  if (subCommandConfig.handler) {
3929
3950
  subParser.setHandler(subCommandConfig.handler);
3930
3951
  }
3931
3952
  return this;
3932
3953
  }
3954
+ /**
3955
+ * Propagates available flags from the current parser to its subcommands,
3956
+ * if those subcommands are configured to inherit recursively (AllParents).
3957
+ */
3958
+ #propagateFlagsRecursive(parser) {
3959
+ for (const [_, subConfig] of parser.getSubCommands()) {
3960
+ const childParser = subConfig.parser;
3961
+ if (!(childParser instanceof ArgParserBase)) continue;
3962
+ const isAllParents = childParser.#inheritParentFlags === FlagInheritance.AllParents;
3963
+ if (isAllParents) {
3964
+ const parentFlags = parser.#flagManager.flags;
3965
+ let flagsAdded = false;
3966
+ for (const parentFlag of parentFlags) {
3967
+ if (!childParser.#flagManager.hasFlag(parentFlag["name"])) {
3968
+ childParser.#flagManager._setProcessedFlagForInheritance(parentFlag);
3969
+ flagsAdded = true;
3970
+ }
3971
+ }
3972
+ if (flagsAdded) {
3973
+ this.#propagateFlagsRecursive(childParser);
3974
+ }
3975
+ }
3976
+ }
3977
+ }
3933
3978
  /**
3934
3979
  * Sets the handler function for this specific parser instance.
3935
3980
  * This handler will be executed if this parser is the final one
@@ -4592,16 +4637,29 @@ class ArgParserBase {
4592
4637
  finalParseResultArgs["$commandChain"] = commandChainSoFar;
4593
4638
  }
4594
4639
  let handlerToExecute = void 0;
4640
+ const handlerContext = {
4641
+ args: currentLevelArgs,
4642
+ parentArgs: accumulatedParentArgs,
4643
+ commandChain: commandChainSoFar,
4644
+ parser: currentParser,
4645
+ parentParser,
4646
+ // displayHelp implementation that respects autoExit setting
4647
+ displayHelp: () => {
4648
+ console.log(currentParser.helpText());
4649
+ if (currentParser.getAutoExit() && typeof process === "object" && typeof process.exit === "function") {
4650
+ process.exit(0);
4651
+ }
4652
+ }
4653
+ };
4595
4654
  if (currentParser.#handler) {
4596
4655
  handlerToExecute = {
4597
4656
  handler: currentParser.#handler,
4598
- context: {
4599
- args: currentLevelArgs,
4600
- parentArgs: accumulatedParentArgs,
4601
- commandChain: commandChainSoFar,
4602
- parser: currentParser,
4603
- parentParser
4604
- }
4657
+ context: handlerContext
4658
+ };
4659
+ } else if (currentParser.#triggerAutoHelpIfNoHandler) {
4660
+ handlerToExecute = {
4661
+ handler: (ctx) => ctx.displayHelp(),
4662
+ context: handlerContext
4605
4663
  };
4606
4664
  }
4607
4665
  return { finalArgs: finalParseResultArgs, handlerToExecute };
@@ -5716,6 +5774,9 @@ ${simpleChalk.dim(`Try '${commandNameToSuggest} --help' for usage details.`)}`
5716
5774
  return options;
5717
5775
  }
5718
5776
  }
5777
+ const autoHelpHandler = async (ctx) => {
5778
+ ctx.displayHelp();
5779
+ };
5719
5780
  function sanitizeMcpToolName(name) {
5720
5781
  if (!name || typeof name !== "string") {
5721
5782
  throw new Error("Tool name must be a non-empty string");
@@ -6203,7 +6264,12 @@ function generateMcpToolsFromArgParser(rootParser, options) {
6203
6264
  parser: rootParser,
6204
6265
  parentArgs: void 0,
6205
6266
  isMcp: true,
6206
- getFlag
6267
+ getFlag,
6268
+ displayHelp: () => {
6269
+ console.error(
6270
+ "Help display is not supported in MCP mode."
6271
+ );
6272
+ }
6207
6273
  };
6208
6274
  const handlerResult = await toolConfig.handler(handlerContext);
6209
6275
  if (process.env["MCP_DEBUG"]) {
@@ -6444,7 +6510,12 @@ function generateMcpToolsFromArgParser(rootParser, options) {
6444
6510
  parser: finalParser,
6445
6511
  parentArgs: resolvedParentArgs,
6446
6512
  isMcp: true,
6447
- getFlag
6513
+ getFlag,
6514
+ displayHelp: () => {
6515
+ console.error(
6516
+ "Help display is not supported in MCP mode."
6517
+ );
6518
+ }
6448
6519
  };
6449
6520
  try {
6450
6521
  handlerResponse = await handlerToCall(handlerContext);
@@ -7129,7 +7200,10 @@ Migration guide: https://github.com/alcyone-labs/arg-parser/blob/main/docs/MCP-M
7129
7200
  parentArgs: {},
7130
7201
  commandChain: [toolConfig.name],
7131
7202
  parser: this,
7132
- isMcp: true
7203
+ isMcp: true,
7204
+ displayHelp: () => {
7205
+ console.error("Help display is not supported in MCP mode.");
7206
+ }
7133
7207
  };
7134
7208
  const startTime = Date.now();
7135
7209
  const result = await toolConfig.handler(context);
@@ -9301,12 +9375,61 @@ class List extends Component {
9301
9375
  }
9302
9376
  }
9303
9377
  }
9378
+ function stripAnsi(str) {
9379
+ return str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
9380
+ }
9381
+ function visualLength(str) {
9382
+ return stripAnsi(str).length;
9383
+ }
9384
+ function wrapText(text, width) {
9385
+ if (width <= 0) return [text];
9386
+ const result = [];
9387
+ const lines = text.split("\n");
9388
+ for (const line of lines) {
9389
+ if (visualLength(line) <= width) {
9390
+ result.push(line);
9391
+ continue;
9392
+ }
9393
+ let currentLine = "";
9394
+ let currentVisibleLength = 0;
9395
+ let activeAnsiCodes = [];
9396
+ const tokenRegex = /(\u001b\[(?:\d{1,3}(?:;\d{1,3})*)?[mK])|([\s\S])/g;
9397
+ let match;
9398
+ while ((match = tokenRegex.exec(line)) !== null) {
9399
+ const ansiCode = match[1];
9400
+ const char2 = match[2];
9401
+ if (ansiCode) {
9402
+ currentLine += ansiCode;
9403
+ if (ansiCode === "\x1B[0m") {
9404
+ activeAnsiCodes = [];
9405
+ } else if (ansiCode.endsWith("m")) {
9406
+ activeAnsiCodes.push(ansiCode);
9407
+ }
9408
+ } else if (char2) {
9409
+ if (currentVisibleLength >= width) {
9410
+ result.push(currentLine + "\x1B[0m");
9411
+ currentLine = activeAnsiCodes.join("") + char2;
9412
+ currentVisibleLength = 1;
9413
+ } else {
9414
+ currentLine += char2;
9415
+ currentVisibleLength++;
9416
+ }
9417
+ }
9418
+ }
9419
+ if (currentLine.length > 0) {
9420
+ result.push(currentLine + "\x1B[0m");
9421
+ }
9422
+ }
9423
+ return result;
9424
+ }
9304
9425
  class ScrollArea extends Component {
9305
9426
  constructor(config) {
9306
9427
  super(config);
9307
9428
  this.contentLines = [];
9308
9429
  this.scrollOffset = 0;
9430
+ this.wrapText = false;
9309
9431
  this.content = config.content;
9432
+ this.wrapText = !!config.wrapText;
9310
9433
  this.updateContentLines();
9311
9434
  }
9312
9435
  setContent(content) {
@@ -9314,8 +9437,18 @@ class ScrollArea extends Component {
9314
9437
  this.scrollOffset = 0;
9315
9438
  this.updateContentLines();
9316
9439
  }
9440
+ resize(x, y, width, height) {
9441
+ super.resize(x, y, width, height);
9442
+ if (this.wrapText) {
9443
+ this.updateContentLines();
9444
+ }
9445
+ }
9317
9446
  updateContentLines() {
9318
- this.contentLines = this.content.split("\n");
9447
+ if (this.wrapText && this.width > 0) {
9448
+ this.contentLines = wrapText(this.content, Math.max(1, this.width - 2));
9449
+ } else {
9450
+ this.contentLines = this.content.split("\n");
9451
+ }
9319
9452
  }
9320
9453
  render() {
9321
9454
  const lines = [];
@@ -9330,7 +9463,6 @@ class ScrollArea extends Component {
9330
9463
  const maxTop = this.height - scrollbarHeight;
9331
9464
  scrollbarTop = Math.floor(scrollPercent * maxTop);
9332
9465
  }
9333
- const stripAnsi = (str) => str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
9334
9466
  for (let i = 0; i < this.height; i++) {
9335
9467
  const lineContent = visibleLines[i] || "";
9336
9468
  let prefix = "";
@@ -26159,6 +26291,7 @@ export {
26159
26291
  ConfigPluginRegistry,
26160
26292
  DxtPathResolver,
26161
26293
  EnvConfigPlugin,
26294
+ FlagInheritance,
26162
26295
  JsonConfigPlugin,
26163
26296
  Logger,
26164
26297
  OutputSchemaPatterns,
@@ -26167,6 +26300,7 @@ export {
26167
26300
  index$2 as UI,
26168
26301
  YamlConfigPlugin,
26169
26302
  absolutePath,
26303
+ autoHelpHandler,
26170
26304
  convertFlagToJsonSchemaProperty,
26171
26305
  convertFlagsToJsonSchema,
26172
26306
  convertFlagsToZodSchema,