@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.cjs CHANGED
@@ -1412,6 +1412,22 @@ const _DxtPathResolver = class _DxtPathResolver {
1412
1412
  };
1413
1413
  _DxtPathResolver._cachedContext = null;
1414
1414
  let DxtPathResolver = _DxtPathResolver;
1415
+ const FlagInheritance = {
1416
+ /**
1417
+ * No flags are inherited from the parent.
1418
+ */
1419
+ NONE: "none",
1420
+ /**
1421
+ * Inherits flags only from the direct parent at the time of attachment (Snapshot behavior).
1422
+ * Equivalent to `true` in legacy boolean config.
1423
+ */
1424
+ DirectParentOnly: "direct-parent-only",
1425
+ /**
1426
+ * Inherits flags from the entire parent chain, ensuring grandchildren receive root flags
1427
+ * even in bottom-up construction scenarios.
1428
+ */
1429
+ AllParents: "all-parents"
1430
+ };
1415
1431
  const zodDxtOptionsSchema = zod.z.object({
1416
1432
  sensitive: zod.z.boolean().optional().describe(
1417
1433
  "Whether this field should be marked as sensitive in DXT user_config"
@@ -3727,6 +3743,7 @@ class ArgParserBase {
3727
3743
  #dxtGenerator;
3728
3744
  #configurationManager;
3729
3745
  #fuzzyMode = false;
3746
+ #triggerAutoHelpIfNoHandler = false;
3730
3747
  // Track dynamically added flags so we can clean them between parses
3731
3748
  #dynamicFlagNames = /* @__PURE__ */ new Set();
3732
3749
  // MCP-related managers
@@ -3754,6 +3771,7 @@ class ArgParserBase {
3754
3771
  this.#handleErrors = options.handleErrors ?? true;
3755
3772
  this.#autoExit = options.autoExit ?? true;
3756
3773
  this.#inheritParentFlags = options.inheritParentFlags ?? false;
3774
+ this.#triggerAutoHelpIfNoHandler = options.triggerAutoHelpIfNoHandler ?? false;
3757
3775
  this.#description = options.description;
3758
3776
  this.#handler = options.handler;
3759
3777
  this.#appCommandName = options.appCommandName;
@@ -3958,7 +3976,9 @@ class ArgParserBase {
3958
3976
  subParser.#appCommandName = this.#appCommandName;
3959
3977
  }
3960
3978
  subParser.#autoExit = this.#autoExit;
3961
- if (subParser.#inheritParentFlags) {
3979
+ subParser.#triggerAutoHelpIfNoHandler = this.#triggerAutoHelpIfNoHandler;
3980
+ const shouldInherit = subParser.#inheritParentFlags === true || subParser.#inheritParentFlags === FlagInheritance.DirectParentOnly || subParser.#inheritParentFlags === FlagInheritance.AllParents;
3981
+ if (shouldInherit) {
3962
3982
  const parentFlags = this.#flagManager.flags;
3963
3983
  for (const parentFlag of parentFlags) {
3964
3984
  if (!subParser.#flagManager.hasFlag(parentFlag["name"])) {
@@ -3967,11 +3987,36 @@ class ArgParserBase {
3967
3987
  }
3968
3988
  }
3969
3989
  this.#subCommands.set(subCommandConfig.name, subCommandConfig);
3990
+ this.#propagateFlagsRecursive(subParser);
3970
3991
  if (subCommandConfig.handler) {
3971
3992
  subParser.setHandler(subCommandConfig.handler);
3972
3993
  }
3973
3994
  return this;
3974
3995
  }
3996
+ /**
3997
+ * Propagates available flags from the current parser to its subcommands,
3998
+ * if those subcommands are configured to inherit recursively (AllParents).
3999
+ */
4000
+ #propagateFlagsRecursive(parser) {
4001
+ for (const [_, subConfig] of parser.getSubCommands()) {
4002
+ const childParser = subConfig.parser;
4003
+ if (!(childParser instanceof ArgParserBase)) continue;
4004
+ const isAllParents = childParser.#inheritParentFlags === FlagInheritance.AllParents;
4005
+ if (isAllParents) {
4006
+ const parentFlags = parser.#flagManager.flags;
4007
+ let flagsAdded = false;
4008
+ for (const parentFlag of parentFlags) {
4009
+ if (!childParser.#flagManager.hasFlag(parentFlag["name"])) {
4010
+ childParser.#flagManager._setProcessedFlagForInheritance(parentFlag);
4011
+ flagsAdded = true;
4012
+ }
4013
+ }
4014
+ if (flagsAdded) {
4015
+ this.#propagateFlagsRecursive(childParser);
4016
+ }
4017
+ }
4018
+ }
4019
+ }
3975
4020
  /**
3976
4021
  * Sets the handler function for this specific parser instance.
3977
4022
  * This handler will be executed if this parser is the final one
@@ -4634,16 +4679,29 @@ class ArgParserBase {
4634
4679
  finalParseResultArgs["$commandChain"] = commandChainSoFar;
4635
4680
  }
4636
4681
  let handlerToExecute = void 0;
4682
+ const handlerContext = {
4683
+ args: currentLevelArgs,
4684
+ parentArgs: accumulatedParentArgs,
4685
+ commandChain: commandChainSoFar,
4686
+ parser: currentParser,
4687
+ parentParser,
4688
+ // displayHelp implementation that respects autoExit setting
4689
+ displayHelp: () => {
4690
+ console.log(currentParser.helpText());
4691
+ if (currentParser.getAutoExit() && typeof process === "object" && typeof process.exit === "function") {
4692
+ process.exit(0);
4693
+ }
4694
+ }
4695
+ };
4637
4696
  if (currentParser.#handler) {
4638
4697
  handlerToExecute = {
4639
4698
  handler: currentParser.#handler,
4640
- context: {
4641
- args: currentLevelArgs,
4642
- parentArgs: accumulatedParentArgs,
4643
- commandChain: commandChainSoFar,
4644
- parser: currentParser,
4645
- parentParser
4646
- }
4699
+ context: handlerContext
4700
+ };
4701
+ } else if (currentParser.#triggerAutoHelpIfNoHandler) {
4702
+ handlerToExecute = {
4703
+ handler: (ctx) => ctx.displayHelp(),
4704
+ context: handlerContext
4647
4705
  };
4648
4706
  }
4649
4707
  return { finalArgs: finalParseResultArgs, handlerToExecute };
@@ -5758,6 +5816,9 @@ ${simpleChalk.dim(`Try '${commandNameToSuggest} --help' for usage details.`)}`
5758
5816
  return options;
5759
5817
  }
5760
5818
  }
5819
+ const autoHelpHandler = async (ctx) => {
5820
+ ctx.displayHelp();
5821
+ };
5761
5822
  function sanitizeMcpToolName(name) {
5762
5823
  if (!name || typeof name !== "string") {
5763
5824
  throw new Error("Tool name must be a non-empty string");
@@ -6245,7 +6306,12 @@ function generateMcpToolsFromArgParser(rootParser, options) {
6245
6306
  parser: rootParser,
6246
6307
  parentArgs: void 0,
6247
6308
  isMcp: true,
6248
- getFlag
6309
+ getFlag,
6310
+ displayHelp: () => {
6311
+ console.error(
6312
+ "Help display is not supported in MCP mode."
6313
+ );
6314
+ }
6249
6315
  };
6250
6316
  const handlerResult = await toolConfig.handler(handlerContext);
6251
6317
  if (process.env["MCP_DEBUG"]) {
@@ -6486,7 +6552,12 @@ function generateMcpToolsFromArgParser(rootParser, options) {
6486
6552
  parser: finalParser,
6487
6553
  parentArgs: resolvedParentArgs,
6488
6554
  isMcp: true,
6489
- getFlag
6555
+ getFlag,
6556
+ displayHelp: () => {
6557
+ console.error(
6558
+ "Help display is not supported in MCP mode."
6559
+ );
6560
+ }
6490
6561
  };
6491
6562
  try {
6492
6563
  handlerResponse = await handlerToCall(handlerContext);
@@ -7171,7 +7242,10 @@ Migration guide: https://github.com/alcyone-labs/arg-parser/blob/main/docs/MCP-M
7171
7242
  parentArgs: {},
7172
7243
  commandChain: [toolConfig.name],
7173
7244
  parser: this,
7174
- isMcp: true
7245
+ isMcp: true,
7246
+ displayHelp: () => {
7247
+ console.error("Help display is not supported in MCP mode.");
7248
+ }
7175
7249
  };
7176
7250
  const startTime = Date.now();
7177
7251
  const result = await toolConfig.handler(context);
@@ -9343,12 +9417,61 @@ class List extends Component {
9343
9417
  }
9344
9418
  }
9345
9419
  }
9420
+ function stripAnsi(str) {
9421
+ return str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
9422
+ }
9423
+ function visualLength(str) {
9424
+ return stripAnsi(str).length;
9425
+ }
9426
+ function wrapText(text, width) {
9427
+ if (width <= 0) return [text];
9428
+ const result = [];
9429
+ const lines = text.split("\n");
9430
+ for (const line of lines) {
9431
+ if (visualLength(line) <= width) {
9432
+ result.push(line);
9433
+ continue;
9434
+ }
9435
+ let currentLine = "";
9436
+ let currentVisibleLength = 0;
9437
+ let activeAnsiCodes = [];
9438
+ const tokenRegex = /(\u001b\[(?:\d{1,3}(?:;\d{1,3})*)?[mK])|([\s\S])/g;
9439
+ let match;
9440
+ while ((match = tokenRegex.exec(line)) !== null) {
9441
+ const ansiCode = match[1];
9442
+ const char = match[2];
9443
+ if (ansiCode) {
9444
+ currentLine += ansiCode;
9445
+ if (ansiCode === "\x1B[0m") {
9446
+ activeAnsiCodes = [];
9447
+ } else if (ansiCode.endsWith("m")) {
9448
+ activeAnsiCodes.push(ansiCode);
9449
+ }
9450
+ } else if (char) {
9451
+ if (currentVisibleLength >= width) {
9452
+ result.push(currentLine + "\x1B[0m");
9453
+ currentLine = activeAnsiCodes.join("") + char;
9454
+ currentVisibleLength = 1;
9455
+ } else {
9456
+ currentLine += char;
9457
+ currentVisibleLength++;
9458
+ }
9459
+ }
9460
+ }
9461
+ if (currentLine.length > 0) {
9462
+ result.push(currentLine + "\x1B[0m");
9463
+ }
9464
+ }
9465
+ return result;
9466
+ }
9346
9467
  class ScrollArea extends Component {
9347
9468
  constructor(config) {
9348
9469
  super(config);
9349
9470
  this.contentLines = [];
9350
9471
  this.scrollOffset = 0;
9472
+ this.wrapText = false;
9351
9473
  this.content = config.content;
9474
+ this.wrapText = !!config.wrapText;
9352
9475
  this.updateContentLines();
9353
9476
  }
9354
9477
  setContent(content) {
@@ -9356,8 +9479,18 @@ class ScrollArea extends Component {
9356
9479
  this.scrollOffset = 0;
9357
9480
  this.updateContentLines();
9358
9481
  }
9482
+ resize(x, y, width, height) {
9483
+ super.resize(x, y, width, height);
9484
+ if (this.wrapText) {
9485
+ this.updateContentLines();
9486
+ }
9487
+ }
9359
9488
  updateContentLines() {
9360
- this.contentLines = this.content.split("\n");
9489
+ if (this.wrapText && this.width > 0) {
9490
+ this.contentLines = wrapText(this.content, Math.max(1, this.width - 2));
9491
+ } else {
9492
+ this.contentLines = this.content.split("\n");
9493
+ }
9361
9494
  }
9362
9495
  render() {
9363
9496
  const lines = [];
@@ -9372,7 +9505,6 @@ class ScrollArea extends Component {
9372
9505
  const maxTop = this.height - scrollbarHeight;
9373
9506
  scrollbarTop = Math.floor(scrollPercent * maxTop);
9374
9507
  }
9375
- const stripAnsi = (str) => str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
9376
9508
  for (let i = 0; i < this.height; i++) {
9377
9509
  const lineContent = visibleLines[i] || "";
9378
9510
  let prefix = "";
@@ -26216,6 +26348,7 @@ exports.ConfigPlugin = ConfigPlugin;
26216
26348
  exports.ConfigPluginRegistry = ConfigPluginRegistry;
26217
26349
  exports.DxtPathResolver = DxtPathResolver;
26218
26350
  exports.EnvConfigPlugin = EnvConfigPlugin;
26351
+ exports.FlagInheritance = FlagInheritance;
26219
26352
  exports.JsonConfigPlugin = JsonConfigPlugin;
26220
26353
  exports.OutputSchemaPatterns = OutputSchemaPatterns;
26221
26354
  exports.SimpleChalk = simpleChalk;
@@ -26223,6 +26356,7 @@ exports.TomlConfigPlugin = TomlConfigPlugin;
26223
26356
  exports.UI = index$2;
26224
26357
  exports.YamlConfigPlugin = YamlConfigPlugin;
26225
26358
  exports.absolutePath = absolutePath;
26359
+ exports.autoHelpHandler = autoHelpHandler;
26226
26360
  exports.convertFlagToJsonSchemaProperty = convertFlagToJsonSchemaProperty;
26227
26361
  exports.convertFlagsToJsonSchema = convertFlagsToJsonSchema;
26228
26362
  exports.convertFlagsToZodSchema = convertFlagsToZodSchema;