@cerefox/memory 1.0.5 → 1.1.0-beta.1

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.
@@ -5340,7 +5340,7 @@ var init_cli_core = __esm(() => {
5340
5340
  init_prompts();
5341
5341
  });
5342
5342
 
5343
- // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/error.js
5343
+ // ../../node_modules/.bun/commander@14.0.3/node_modules/commander/lib/error.js
5344
5344
  var require_error = __commonJS((exports) => {
5345
5345
  class CommanderError extends Error {
5346
5346
  constructor(exitCode, code, message) {
@@ -5364,7 +5364,7 @@ var require_error = __commonJS((exports) => {
5364
5364
  exports.InvalidArgumentError = InvalidArgumentError;
5365
5365
  });
5366
5366
 
5367
- // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/argument.js
5367
+ // ../../node_modules/.bun/commander@14.0.3/node_modules/commander/lib/argument.js
5368
5368
  var require_argument = __commonJS((exports) => {
5369
5369
  var { InvalidArgumentError } = require_error();
5370
5370
 
@@ -5390,7 +5390,7 @@ var require_argument = __commonJS((exports) => {
5390
5390
  this._name = name;
5391
5391
  break;
5392
5392
  }
5393
- if (this._name.length > 3 && this._name.slice(-3) === "...") {
5393
+ if (this._name.endsWith("...")) {
5394
5394
  this.variadic = true;
5395
5395
  this._name = this._name.slice(0, -3);
5396
5396
  }
@@ -5398,11 +5398,12 @@ var require_argument = __commonJS((exports) => {
5398
5398
  name() {
5399
5399
  return this._name;
5400
5400
  }
5401
- _concatValue(value, previous) {
5401
+ _collectValue(value, previous) {
5402
5402
  if (previous === this.defaultValue || !Array.isArray(previous)) {
5403
5403
  return [value];
5404
5404
  }
5405
- return previous.concat(value);
5405
+ previous.push(value);
5406
+ return previous;
5406
5407
  }
5407
5408
  default(value, description) {
5408
5409
  this.defaultValue = value;
@@ -5420,7 +5421,7 @@ var require_argument = __commonJS((exports) => {
5420
5421
  throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(", ")}.`);
5421
5422
  }
5422
5423
  if (this.variadic) {
5423
- return this._concatValue(arg, previous);
5424
+ return this._collectValue(arg, previous);
5424
5425
  }
5425
5426
  return arg;
5426
5427
  };
@@ -5443,17 +5444,21 @@ var require_argument = __commonJS((exports) => {
5443
5444
  exports.humanReadableArgName = humanReadableArgName;
5444
5445
  });
5445
5446
 
5446
- // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/help.js
5447
+ // ../../node_modules/.bun/commander@14.0.3/node_modules/commander/lib/help.js
5447
5448
  var require_help = __commonJS((exports) => {
5448
5449
  var { humanReadableArgName } = require_argument();
5449
5450
 
5450
5451
  class Help {
5451
5452
  constructor() {
5452
5453
  this.helpWidth = undefined;
5454
+ this.minWidthToWrap = 40;
5453
5455
  this.sortSubcommands = false;
5454
5456
  this.sortOptions = false;
5455
5457
  this.showGlobalOptions = false;
5456
5458
  }
5459
+ prepareContext(contextOptions) {
5460
+ this.helpWidth = this.helpWidth ?? contextOptions.helpWidth ?? 80;
5461
+ }
5457
5462
  visibleCommands(cmd) {
5458
5463
  const visibleCommands = cmd.commands.filter((cmd2) => !cmd2._hidden);
5459
5464
  const helpCommand = cmd._getHelpCommand();
@@ -5528,22 +5533,22 @@ var require_help = __commonJS((exports) => {
5528
5533
  }
5529
5534
  longestSubcommandTermLength(cmd, helper) {
5530
5535
  return helper.visibleCommands(cmd).reduce((max, command) => {
5531
- return Math.max(max, helper.subcommandTerm(command).length);
5536
+ return Math.max(max, this.displayWidth(helper.styleSubcommandTerm(helper.subcommandTerm(command))));
5532
5537
  }, 0);
5533
5538
  }
5534
5539
  longestOptionTermLength(cmd, helper) {
5535
5540
  return helper.visibleOptions(cmd).reduce((max, option) => {
5536
- return Math.max(max, helper.optionTerm(option).length);
5541
+ return Math.max(max, this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))));
5537
5542
  }, 0);
5538
5543
  }
5539
5544
  longestGlobalOptionTermLength(cmd, helper) {
5540
5545
  return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
5541
- return Math.max(max, helper.optionTerm(option).length);
5546
+ return Math.max(max, this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))));
5542
5547
  }, 0);
5543
5548
  }
5544
5549
  longestArgumentTermLength(cmd, helper) {
5545
5550
  return helper.visibleArguments(cmd).reduce((max, argument) => {
5546
- return Math.max(max, helper.argumentTerm(argument).length);
5551
+ return Math.max(max, this.displayWidth(helper.styleArgumentTerm(helper.argumentTerm(argument))));
5547
5552
  }, 0);
5548
5553
  }
5549
5554
  commandUsage(cmd) {
@@ -5581,7 +5586,11 @@ var require_help = __commonJS((exports) => {
5581
5586
  extraInfo.push(`env: ${option.envVar}`);
5582
5587
  }
5583
5588
  if (extraInfo.length > 0) {
5584
- return `${option.description} (${extraInfo.join(", ")})`;
5589
+ const extraDescription = `(${extraInfo.join(", ")})`;
5590
+ if (option.description) {
5591
+ return `${option.description} ${extraDescription}`;
5592
+ }
5593
+ return extraDescription;
5585
5594
  }
5586
5595
  return option.description;
5587
5596
  }
@@ -5594,105 +5603,205 @@ var require_help = __commonJS((exports) => {
5594
5603
  extraInfo.push(`default: ${argument.defaultValueDescription || JSON.stringify(argument.defaultValue)}`);
5595
5604
  }
5596
5605
  if (extraInfo.length > 0) {
5597
- const extraDescripton = `(${extraInfo.join(", ")})`;
5606
+ const extraDescription = `(${extraInfo.join(", ")})`;
5598
5607
  if (argument.description) {
5599
- return `${argument.description} ${extraDescripton}`;
5608
+ return `${argument.description} ${extraDescription}`;
5600
5609
  }
5601
- return extraDescripton;
5610
+ return extraDescription;
5602
5611
  }
5603
5612
  return argument.description;
5604
5613
  }
5614
+ formatItemList(heading, items, helper) {
5615
+ if (items.length === 0)
5616
+ return [];
5617
+ return [helper.styleTitle(heading), ...items, ""];
5618
+ }
5619
+ groupItems(unsortedItems, visibleItems, getGroup) {
5620
+ const result = new Map;
5621
+ unsortedItems.forEach((item) => {
5622
+ const group = getGroup(item);
5623
+ if (!result.has(group))
5624
+ result.set(group, []);
5625
+ });
5626
+ visibleItems.forEach((item) => {
5627
+ const group = getGroup(item);
5628
+ if (!result.has(group)) {
5629
+ result.set(group, []);
5630
+ }
5631
+ result.get(group).push(item);
5632
+ });
5633
+ return result;
5634
+ }
5605
5635
  formatHelp(cmd, helper) {
5606
5636
  const termWidth = helper.padWidth(cmd, helper);
5607
- const helpWidth = helper.helpWidth || 80;
5608
- const itemIndentWidth = 2;
5609
- const itemSeparatorWidth = 2;
5610
- function formatItem(term, description) {
5611
- if (description) {
5612
- const fullText = `${term.padEnd(termWidth + itemSeparatorWidth)}${description}`;
5613
- return helper.wrap(fullText, helpWidth - itemIndentWidth, termWidth + itemSeparatorWidth);
5614
- }
5615
- return term;
5616
- }
5617
- function formatList(textArray) {
5618
- return textArray.join(`
5619
- `).replace(/^/gm, " ".repeat(itemIndentWidth));
5637
+ const helpWidth = helper.helpWidth ?? 80;
5638
+ function callFormatItem(term, description) {
5639
+ return helper.formatItem(term, termWidth, description, helper);
5620
5640
  }
5621
- let output = [`Usage: ${helper.commandUsage(cmd)}`, ""];
5641
+ let output = [
5642
+ `${helper.styleTitle("Usage:")} ${helper.styleUsage(helper.commandUsage(cmd))}`,
5643
+ ""
5644
+ ];
5622
5645
  const commandDescription = helper.commandDescription(cmd);
5623
5646
  if (commandDescription.length > 0) {
5624
5647
  output = output.concat([
5625
- helper.wrap(commandDescription, helpWidth, 0),
5648
+ helper.boxWrap(helper.styleCommandDescription(commandDescription), helpWidth),
5626
5649
  ""
5627
5650
  ]);
5628
5651
  }
5629
5652
  const argumentList = helper.visibleArguments(cmd).map((argument) => {
5630
- return formatItem(helper.argumentTerm(argument), helper.argumentDescription(argument));
5653
+ return callFormatItem(helper.styleArgumentTerm(helper.argumentTerm(argument)), helper.styleArgumentDescription(helper.argumentDescription(argument)));
5631
5654
  });
5632
- if (argumentList.length > 0) {
5633
- output = output.concat(["Arguments:", formatList(argumentList), ""]);
5634
- }
5635
- const optionList = helper.visibleOptions(cmd).map((option) => {
5636
- return formatItem(helper.optionTerm(option), helper.optionDescription(option));
5655
+ output = output.concat(this.formatItemList("Arguments:", argumentList, helper));
5656
+ const optionGroups = this.groupItems(cmd.options, helper.visibleOptions(cmd), (option) => option.helpGroupHeading ?? "Options:");
5657
+ optionGroups.forEach((options, group) => {
5658
+ const optionList = options.map((option) => {
5659
+ return callFormatItem(helper.styleOptionTerm(helper.optionTerm(option)), helper.styleOptionDescription(helper.optionDescription(option)));
5660
+ });
5661
+ output = output.concat(this.formatItemList(group, optionList, helper));
5637
5662
  });
5638
- if (optionList.length > 0) {
5639
- output = output.concat(["Options:", formatList(optionList), ""]);
5640
- }
5641
- if (this.showGlobalOptions) {
5663
+ if (helper.showGlobalOptions) {
5642
5664
  const globalOptionList = helper.visibleGlobalOptions(cmd).map((option) => {
5643
- return formatItem(helper.optionTerm(option), helper.optionDescription(option));
5665
+ return callFormatItem(helper.styleOptionTerm(helper.optionTerm(option)), helper.styleOptionDescription(helper.optionDescription(option)));
5644
5666
  });
5645
- if (globalOptionList.length > 0) {
5646
- output = output.concat([
5647
- "Global Options:",
5648
- formatList(globalOptionList),
5649
- ""
5650
- ]);
5651
- }
5667
+ output = output.concat(this.formatItemList("Global Options:", globalOptionList, helper));
5652
5668
  }
5653
- const commandList = helper.visibleCommands(cmd).map((cmd2) => {
5654
- return formatItem(helper.subcommandTerm(cmd2), helper.subcommandDescription(cmd2));
5669
+ const commandGroups = this.groupItems(cmd.commands, helper.visibleCommands(cmd), (sub) => sub.helpGroup() || "Commands:");
5670
+ commandGroups.forEach((commands, group) => {
5671
+ const commandList = commands.map((sub) => {
5672
+ return callFormatItem(helper.styleSubcommandTerm(helper.subcommandTerm(sub)), helper.styleSubcommandDescription(helper.subcommandDescription(sub)));
5673
+ });
5674
+ output = output.concat(this.formatItemList(group, commandList, helper));
5655
5675
  });
5656
- if (commandList.length > 0) {
5657
- output = output.concat(["Commands:", formatList(commandList), ""]);
5658
- }
5659
5676
  return output.join(`
5660
5677
  `);
5661
5678
  }
5679
+ displayWidth(str) {
5680
+ return stripColor(str).length;
5681
+ }
5682
+ styleTitle(str) {
5683
+ return str;
5684
+ }
5685
+ styleUsage(str) {
5686
+ return str.split(" ").map((word) => {
5687
+ if (word === "[options]")
5688
+ return this.styleOptionText(word);
5689
+ if (word === "[command]")
5690
+ return this.styleSubcommandText(word);
5691
+ if (word[0] === "[" || word[0] === "<")
5692
+ return this.styleArgumentText(word);
5693
+ return this.styleCommandText(word);
5694
+ }).join(" ");
5695
+ }
5696
+ styleCommandDescription(str) {
5697
+ return this.styleDescriptionText(str);
5698
+ }
5699
+ styleOptionDescription(str) {
5700
+ return this.styleDescriptionText(str);
5701
+ }
5702
+ styleSubcommandDescription(str) {
5703
+ return this.styleDescriptionText(str);
5704
+ }
5705
+ styleArgumentDescription(str) {
5706
+ return this.styleDescriptionText(str);
5707
+ }
5708
+ styleDescriptionText(str) {
5709
+ return str;
5710
+ }
5711
+ styleOptionTerm(str) {
5712
+ return this.styleOptionText(str);
5713
+ }
5714
+ styleSubcommandTerm(str) {
5715
+ return str.split(" ").map((word) => {
5716
+ if (word === "[options]")
5717
+ return this.styleOptionText(word);
5718
+ if (word[0] === "[" || word[0] === "<")
5719
+ return this.styleArgumentText(word);
5720
+ return this.styleSubcommandText(word);
5721
+ }).join(" ");
5722
+ }
5723
+ styleArgumentTerm(str) {
5724
+ return this.styleArgumentText(str);
5725
+ }
5726
+ styleOptionText(str) {
5727
+ return str;
5728
+ }
5729
+ styleArgumentText(str) {
5730
+ return str;
5731
+ }
5732
+ styleSubcommandText(str) {
5733
+ return str;
5734
+ }
5735
+ styleCommandText(str) {
5736
+ return str;
5737
+ }
5662
5738
  padWidth(cmd, helper) {
5663
5739
  return Math.max(helper.longestOptionTermLength(cmd, helper), helper.longestGlobalOptionTermLength(cmd, helper), helper.longestSubcommandTermLength(cmd, helper), helper.longestArgumentTermLength(cmd, helper));
5664
5740
  }
5665
- wrap(str, width, indent, minColumnWidth = 40) {
5666
- const indents = " \\f\\t\\v   -    \uFEFF";
5667
- const manualIndent = new RegExp(`[\\n][${indents}]+`);
5668
- if (str.match(manualIndent))
5669
- return str;
5670
- const columnWidth = width - indent;
5671
- if (columnWidth < minColumnWidth)
5741
+ preformatted(str) {
5742
+ return /\n[^\S\r\n]/.test(str);
5743
+ }
5744
+ formatItem(term, termWidth, description, helper) {
5745
+ const itemIndent = 2;
5746
+ const itemIndentStr = " ".repeat(itemIndent);
5747
+ if (!description)
5748
+ return itemIndentStr + term;
5749
+ const paddedTerm = term.padEnd(termWidth + term.length - helper.displayWidth(term));
5750
+ const spacerWidth = 2;
5751
+ const helpWidth = this.helpWidth ?? 80;
5752
+ const remainingWidth = helpWidth - termWidth - spacerWidth - itemIndent;
5753
+ let formattedDescription;
5754
+ if (remainingWidth < this.minWidthToWrap || helper.preformatted(description)) {
5755
+ formattedDescription = description;
5756
+ } else {
5757
+ const wrappedDescription = helper.boxWrap(description, remainingWidth);
5758
+ formattedDescription = wrappedDescription.replace(/\n/g, `
5759
+ ` + " ".repeat(termWidth + spacerWidth));
5760
+ }
5761
+ return itemIndentStr + paddedTerm + " ".repeat(spacerWidth) + formattedDescription.replace(/\n/g, `
5762
+ ${itemIndentStr}`);
5763
+ }
5764
+ boxWrap(str, width) {
5765
+ if (width < this.minWidthToWrap)
5672
5766
  return str;
5673
- const leadingStr = str.slice(0, indent);
5674
- const columnText = str.slice(indent).replace(`\r
5675
- `, `
5676
- `);
5677
- const indentString = " ".repeat(indent);
5678
- const zeroWidthSpace = "​";
5679
- const breaks = `\\s${zeroWidthSpace}`;
5680
- const regex = new RegExp(`
5681
- |.{1,${columnWidth - 1}}([${breaks}]|$)|[^${breaks}]+?([${breaks}]|$)`, "g");
5682
- const lines = columnText.match(regex) || [];
5683
- return leadingStr + lines.map((line, i) => {
5684
- if (line === `
5685
- `)
5686
- return "";
5687
- return (i > 0 ? indentString : "") + line.trimEnd();
5688
- }).join(`
5767
+ const rawLines = str.split(/\r\n|\n/);
5768
+ const chunkPattern = /[\s]*[^\s]+/g;
5769
+ const wrappedLines = [];
5770
+ rawLines.forEach((line) => {
5771
+ const chunks = line.match(chunkPattern);
5772
+ if (chunks === null) {
5773
+ wrappedLines.push("");
5774
+ return;
5775
+ }
5776
+ let sumChunks = [chunks.shift()];
5777
+ let sumWidth = this.displayWidth(sumChunks[0]);
5778
+ chunks.forEach((chunk) => {
5779
+ const visibleWidth = this.displayWidth(chunk);
5780
+ if (sumWidth + visibleWidth <= width) {
5781
+ sumChunks.push(chunk);
5782
+ sumWidth += visibleWidth;
5783
+ return;
5784
+ }
5785
+ wrappedLines.push(sumChunks.join(""));
5786
+ const nextChunk = chunk.trimStart();
5787
+ sumChunks = [nextChunk];
5788
+ sumWidth = this.displayWidth(nextChunk);
5789
+ });
5790
+ wrappedLines.push(sumChunks.join(""));
5791
+ });
5792
+ return wrappedLines.join(`
5689
5793
  `);
5690
5794
  }
5691
5795
  }
5796
+ function stripColor(str) {
5797
+ const sgrPattern = /\x1b\[\d*(;\d*)*m/g;
5798
+ return str.replace(sgrPattern, "");
5799
+ }
5692
5800
  exports.Help = Help;
5801
+ exports.stripColor = stripColor;
5693
5802
  });
5694
5803
 
5695
- // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/option.js
5804
+ // ../../node_modules/.bun/commander@14.0.3/node_modules/commander/lib/option.js
5696
5805
  var require_option = __commonJS((exports) => {
5697
5806
  var { InvalidArgumentError } = require_error();
5698
5807
 
@@ -5720,6 +5829,7 @@ var require_option = __commonJS((exports) => {
5720
5829
  this.argChoices = undefined;
5721
5830
  this.conflictsWith = [];
5722
5831
  this.implied = undefined;
5832
+ this.helpGroupHeading = undefined;
5723
5833
  }
5724
5834
  default(value, description) {
5725
5835
  this.defaultValue = value;
@@ -5758,11 +5868,12 @@ var require_option = __commonJS((exports) => {
5758
5868
  this.hidden = !!hide;
5759
5869
  return this;
5760
5870
  }
5761
- _concatValue(value, previous) {
5871
+ _collectValue(value, previous) {
5762
5872
  if (previous === this.defaultValue || !Array.isArray(previous)) {
5763
5873
  return [value];
5764
5874
  }
5765
- return previous.concat(value);
5875
+ previous.push(value);
5876
+ return previous;
5766
5877
  }
5767
5878
  choices(values) {
5768
5879
  this.argChoices = values.slice();
@@ -5771,7 +5882,7 @@ var require_option = __commonJS((exports) => {
5771
5882
  throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(", ")}.`);
5772
5883
  }
5773
5884
  if (this.variadic) {
5774
- return this._concatValue(arg, previous);
5885
+ return this._collectValue(arg, previous);
5775
5886
  }
5776
5887
  return arg;
5777
5888
  };
@@ -5784,7 +5895,14 @@ var require_option = __commonJS((exports) => {
5784
5895
  return this.short.replace(/^-/, "");
5785
5896
  }
5786
5897
  attributeName() {
5787
- return camelcase(this.name().replace(/^no-/, ""));
5898
+ if (this.negate) {
5899
+ return camelcase(this.name().replace(/^no-/, ""));
5900
+ }
5901
+ return camelcase(this.name());
5902
+ }
5903
+ helpGroup(heading) {
5904
+ this.helpGroupHeading = heading;
5905
+ return this;
5788
5906
  }
5789
5907
  is(arg) {
5790
5908
  return this.short === arg || this.long === arg;
@@ -5829,21 +5947,45 @@ var require_option = __commonJS((exports) => {
5829
5947
  function splitOptionFlags(flags) {
5830
5948
  let shortFlag;
5831
5949
  let longFlag;
5832
- const flagParts = flags.split(/[ |,]+/);
5833
- if (flagParts.length > 1 && !/^[[<]/.test(flagParts[1]))
5950
+ const shortFlagExp = /^-[^-]$/;
5951
+ const longFlagExp = /^--[^-]/;
5952
+ const flagParts = flags.split(/[ |,]+/).concat("guard");
5953
+ if (shortFlagExp.test(flagParts[0]))
5834
5954
  shortFlag = flagParts.shift();
5835
- longFlag = flagParts.shift();
5836
- if (!shortFlag && /^-[^-]$/.test(longFlag)) {
5955
+ if (longFlagExp.test(flagParts[0]))
5956
+ longFlag = flagParts.shift();
5957
+ if (!shortFlag && shortFlagExp.test(flagParts[0]))
5958
+ shortFlag = flagParts.shift();
5959
+ if (!shortFlag && longFlagExp.test(flagParts[0])) {
5837
5960
  shortFlag = longFlag;
5838
- longFlag = undefined;
5839
- }
5961
+ longFlag = flagParts.shift();
5962
+ }
5963
+ if (flagParts[0].startsWith("-")) {
5964
+ const unsupportedFlag = flagParts[0];
5965
+ const baseError = `option creation failed due to '${unsupportedFlag}' in option flags '${flags}'`;
5966
+ if (/^-[^-][^-]/.test(unsupportedFlag))
5967
+ throw new Error(`${baseError}
5968
+ - a short flag is a single dash and a single character
5969
+ - either use a single dash and a single character (for a short flag)
5970
+ - or use a double dash for a long option (and can have two, like '--ws, --workspace')`);
5971
+ if (shortFlagExp.test(unsupportedFlag))
5972
+ throw new Error(`${baseError}
5973
+ - too many short flags`);
5974
+ if (longFlagExp.test(unsupportedFlag))
5975
+ throw new Error(`${baseError}
5976
+ - too many long flags`);
5977
+ throw new Error(`${baseError}
5978
+ - unrecognised flag format`);
5979
+ }
5980
+ if (shortFlag === undefined && longFlag === undefined)
5981
+ throw new Error(`option creation failed due to no flags found in '${flags}'.`);
5840
5982
  return { shortFlag, longFlag };
5841
5983
  }
5842
5984
  exports.Option = Option;
5843
5985
  exports.DualOptions = DualOptions;
5844
5986
  });
5845
5987
 
5846
- // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/suggestSimilar.js
5988
+ // ../../node_modules/.bun/commander@14.0.3/node_modules/commander/lib/suggestSimilar.js
5847
5989
  var require_suggestSimilar = __commonJS((exports) => {
5848
5990
  var maxDistance = 3;
5849
5991
  function editDistance(a, b) {
@@ -5916,7 +6058,7 @@ var require_suggestSimilar = __commonJS((exports) => {
5916
6058
  exports.suggestSimilar = suggestSimilar;
5917
6059
  });
5918
6060
 
5919
- // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/command.js
6061
+ // ../../node_modules/.bun/commander@14.0.3/node_modules/commander/lib/command.js
5920
6062
  var require_command = __commonJS((exports) => {
5921
6063
  var EventEmitter = __require("node:events").EventEmitter;
5922
6064
  var childProcess = __require("node:child_process");
@@ -5925,7 +6067,7 @@ var require_command = __commonJS((exports) => {
5925
6067
  var process2 = __require("node:process");
5926
6068
  var { Argument, humanReadableArgName } = require_argument();
5927
6069
  var { CommanderError } = require_error();
5928
- var { Help } = require_help();
6070
+ var { Help, stripColor } = require_help();
5929
6071
  var { Option, DualOptions } = require_option();
5930
6072
  var { suggestSimilar } = require_suggestSimilar();
5931
6073
 
@@ -5936,7 +6078,7 @@ var require_command = __commonJS((exports) => {
5936
6078
  this.options = [];
5937
6079
  this.parent = null;
5938
6080
  this._allowUnknownOption = false;
5939
- this._allowExcessArguments = true;
6081
+ this._allowExcessArguments = false;
5940
6082
  this.registeredArguments = [];
5941
6083
  this._args = this.registeredArguments;
5942
6084
  this.args = [];
@@ -5963,18 +6105,25 @@ var require_command = __commonJS((exports) => {
5963
6105
  this._lifeCycleHooks = {};
5964
6106
  this._showHelpAfterError = false;
5965
6107
  this._showSuggestionAfterError = true;
6108
+ this._savedState = null;
5966
6109
  this._outputConfiguration = {
5967
6110
  writeOut: (str) => process2.stdout.write(str),
5968
6111
  writeErr: (str) => process2.stderr.write(str),
6112
+ outputError: (str, write) => write(str),
5969
6113
  getOutHelpWidth: () => process2.stdout.isTTY ? process2.stdout.columns : undefined,
5970
6114
  getErrHelpWidth: () => process2.stderr.isTTY ? process2.stderr.columns : undefined,
5971
- outputError: (str, write) => write(str)
6115
+ getOutHasColors: () => useColor() ?? (process2.stdout.isTTY && process2.stdout.hasColors?.()),
6116
+ getErrHasColors: () => useColor() ?? (process2.stderr.isTTY && process2.stderr.hasColors?.()),
6117
+ stripColor: (str) => stripColor(str)
5972
6118
  };
5973
6119
  this._hidden = false;
5974
6120
  this._helpOption = undefined;
5975
6121
  this._addImplicitHelpCommand = undefined;
5976
6122
  this._helpCommand = undefined;
5977
6123
  this._helpConfiguration = {};
6124
+ this._helpGroupHeading = undefined;
6125
+ this._defaultCommandGroup = undefined;
6126
+ this._defaultOptionGroup = undefined;
5978
6127
  }
5979
6128
  copyInheritedSettings(sourceCommand) {
5980
6129
  this._outputConfiguration = sourceCommand._outputConfiguration;
@@ -6039,7 +6188,10 @@ var require_command = __commonJS((exports) => {
6039
6188
  configureOutput(configuration) {
6040
6189
  if (configuration === undefined)
6041
6190
  return this._outputConfiguration;
6042
- Object.assign(this._outputConfiguration, configuration);
6191
+ this._outputConfiguration = {
6192
+ ...this._outputConfiguration,
6193
+ ...configuration
6194
+ };
6043
6195
  return this;
6044
6196
  }
6045
6197
  showHelpAfterError(displayHelp = true) {
@@ -6070,12 +6222,12 @@ var require_command = __commonJS((exports) => {
6070
6222
  createArgument(name, description) {
6071
6223
  return new Argument(name, description);
6072
6224
  }
6073
- argument(name, description, fn, defaultValue) {
6225
+ argument(name, description, parseArg, defaultValue) {
6074
6226
  const argument = this.createArgument(name, description);
6075
- if (typeof fn === "function") {
6076
- argument.default(defaultValue).argParser(fn);
6227
+ if (typeof parseArg === "function") {
6228
+ argument.default(defaultValue).argParser(parseArg);
6077
6229
  } else {
6078
- argument.default(fn);
6230
+ argument.default(parseArg);
6079
6231
  }
6080
6232
  this.addArgument(argument);
6081
6233
  return this;
@@ -6088,7 +6240,7 @@ var require_command = __commonJS((exports) => {
6088
6240
  }
6089
6241
  addArgument(argument) {
6090
6242
  const previousArgument = this.registeredArguments.slice(-1)[0];
6091
- if (previousArgument && previousArgument.variadic) {
6243
+ if (previousArgument?.variadic) {
6092
6244
  throw new Error(`only the last argument can be variadic '${previousArgument.name()}'`);
6093
6245
  }
6094
6246
  if (argument.required && argument.defaultValue !== undefined && argument.parseArg === undefined) {
@@ -6100,10 +6252,13 @@ var require_command = __commonJS((exports) => {
6100
6252
  helpCommand(enableOrNameAndArgs, description) {
6101
6253
  if (typeof enableOrNameAndArgs === "boolean") {
6102
6254
  this._addImplicitHelpCommand = enableOrNameAndArgs;
6255
+ if (enableOrNameAndArgs && this._defaultCommandGroup) {
6256
+ this._initCommandGroup(this._getHelpCommand());
6257
+ }
6103
6258
  return this;
6104
6259
  }
6105
- enableOrNameAndArgs = enableOrNameAndArgs ?? "help [command]";
6106
- const [, helpName, helpArgs] = enableOrNameAndArgs.match(/([^ ]+) *(.*)/);
6260
+ const nameAndArgs = enableOrNameAndArgs ?? "help [command]";
6261
+ const [, helpName, helpArgs] = nameAndArgs.match(/([^ ]+) *(.*)/);
6107
6262
  const helpDescription = description ?? "display help for command";
6108
6263
  const helpCommand = this.createCommand(helpName);
6109
6264
  helpCommand.helpOption(false);
@@ -6113,6 +6268,8 @@ var require_command = __commonJS((exports) => {
6113
6268
  helpCommand.description(helpDescription);
6114
6269
  this._addImplicitHelpCommand = true;
6115
6270
  this._helpCommand = helpCommand;
6271
+ if (enableOrNameAndArgs || description)
6272
+ this._initCommandGroup(helpCommand);
6116
6273
  return this;
6117
6274
  }
6118
6275
  addHelpCommand(helpCommand, deprecatedDescription) {
@@ -6122,6 +6279,7 @@ var require_command = __commonJS((exports) => {
6122
6279
  }
6123
6280
  this._addImplicitHelpCommand = true;
6124
6281
  this._helpCommand = helpCommand;
6282
+ this._initCommandGroup(helpCommand);
6125
6283
  return this;
6126
6284
  }
6127
6285
  _getHelpCommand() {
@@ -6201,6 +6359,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
6201
6359
  throw new Error(`Cannot add option '${option.flags}'${this._name && ` to command '${this._name}'`} due to conflicting flag '${matchingFlag}'
6202
6360
  - already used by option '${matchingOption.flags}'`);
6203
6361
  }
6362
+ this._initOptionGroup(option);
6204
6363
  this.options.push(option);
6205
6364
  }
6206
6365
  _registerCommand(command) {
@@ -6213,6 +6372,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
6213
6372
  const newCmd = knownBy(command).join("|");
6214
6373
  throw new Error(`cannot add command '${newCmd}' as already have command '${existingCmd}'`);
6215
6374
  }
6375
+ this._initCommandGroup(command);
6216
6376
  this.commands.push(command);
6217
6377
  }
6218
6378
  addOption(option) {
@@ -6235,7 +6395,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
6235
6395
  if (val !== null && option.parseArg) {
6236
6396
  val = this._callParseArg(option, val, oldValue, invalidValueMessage);
6237
6397
  } else if (val !== null && option.variadic) {
6238
- val = option._concatValue(val, oldValue);
6398
+ val = option._collectValue(val, oldValue);
6239
6399
  }
6240
6400
  if (val == null) {
6241
6401
  if (option.negate) {
@@ -6400,15 +6560,53 @@ Expecting one of '${allowedValues.join("', '")}'`);
6400
6560
  return userArgs;
6401
6561
  }
6402
6562
  parse(argv, parseOptions) {
6563
+ this._prepareForParse();
6403
6564
  const userArgs = this._prepareUserArgs(argv, parseOptions);
6404
6565
  this._parseCommand([], userArgs);
6405
6566
  return this;
6406
6567
  }
6407
6568
  async parseAsync(argv, parseOptions) {
6569
+ this._prepareForParse();
6408
6570
  const userArgs = this._prepareUserArgs(argv, parseOptions);
6409
6571
  await this._parseCommand([], userArgs);
6410
6572
  return this;
6411
6573
  }
6574
+ _prepareForParse() {
6575
+ if (this._savedState === null) {
6576
+ this.saveStateBeforeParse();
6577
+ } else {
6578
+ this.restoreStateBeforeParse();
6579
+ }
6580
+ }
6581
+ saveStateBeforeParse() {
6582
+ this._savedState = {
6583
+ _name: this._name,
6584
+ _optionValues: { ...this._optionValues },
6585
+ _optionValueSources: { ...this._optionValueSources }
6586
+ };
6587
+ }
6588
+ restoreStateBeforeParse() {
6589
+ if (this._storeOptionsAsProperties)
6590
+ throw new Error(`Can not call parse again when storeOptionsAsProperties is true.
6591
+ - either make a new Command for each call to parse, or stop storing options as properties`);
6592
+ this._name = this._savedState._name;
6593
+ this._scriptPath = null;
6594
+ this.rawArgs = [];
6595
+ this._optionValues = { ...this._savedState._optionValues };
6596
+ this._optionValueSources = { ...this._savedState._optionValueSources };
6597
+ this.args = [];
6598
+ this.processedArgs = [];
6599
+ }
6600
+ _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
6601
+ if (fs.existsSync(executableFile))
6602
+ return;
6603
+ const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
6604
+ const executableMissing = `'${executableFile}' does not exist
6605
+ - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
6606
+ - if the default executable name is not suitable, use the executableFile option to supply a custom name or path
6607
+ - ${executableDirMessage}`;
6608
+ throw new Error(executableMissing);
6609
+ }
6412
6610
  _executeSubCommand(subcommand, args) {
6413
6611
  args = args.slice();
6414
6612
  let launchWithNode = false;
@@ -6432,7 +6630,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
6432
6630
  let resolvedScriptPath;
6433
6631
  try {
6434
6632
  resolvedScriptPath = fs.realpathSync(this._scriptPath);
6435
- } catch (err) {
6633
+ } catch {
6436
6634
  resolvedScriptPath = this._scriptPath;
6437
6635
  }
6438
6636
  executableDir = path.resolve(path.dirname(resolvedScriptPath), executableDir);
@@ -6458,6 +6656,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
6458
6656
  proc = childProcess.spawn(executableFile, args, { stdio: "inherit" });
6459
6657
  }
6460
6658
  } else {
6659
+ this._checkForMissingExecutable(executableFile, executableDir, subcommand._name);
6461
6660
  args.unshift(executableFile);
6462
6661
  args = incrementNodeInspectorPort(process2.execArgv).concat(args);
6463
6662
  proc = childProcess.spawn(process2.execPath, args, { stdio: "inherit" });
@@ -6483,12 +6682,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
6483
6682
  });
6484
6683
  proc.on("error", (err) => {
6485
6684
  if (err.code === "ENOENT") {
6486
- const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
6487
- const executableMissing = `'${executableFile}' does not exist
6488
- - if '${subcommand._name}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
6489
- - if the default executable name is not suitable, use the executableFile option to supply a custom name or path
6490
- - ${executableDirMessage}`;
6491
- throw new Error(executableMissing);
6685
+ this._checkForMissingExecutable(executableFile, executableDir, subcommand._name);
6492
6686
  } else if (err.code === "EACCES") {
6493
6687
  throw new Error(`'${executableFile}' not executable`);
6494
6688
  }
@@ -6506,6 +6700,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
6506
6700
  const subCommand = this._findCommand(commandName);
6507
6701
  if (!subCommand)
6508
6702
  this.help({ error: true });
6703
+ subCommand._prepareForParse();
6509
6704
  let promiseChain;
6510
6705
  promiseChain = this._chainOrCallSubCommandHook(promiseChain, subCommand, "preSubcommand");
6511
6706
  promiseChain = this._chainOrCall(promiseChain, () => {
@@ -6575,7 +6770,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
6575
6770
  this.processedArgs = processedArgs;
6576
6771
  }
6577
6772
  _chainOrCall(promise, fn) {
6578
- if (promise && promise.then && typeof promise.then === "function") {
6773
+ if (promise?.then && typeof promise.then === "function") {
6579
6774
  return promise.then(() => fn());
6580
6775
  }
6581
6776
  return fn();
@@ -6652,7 +6847,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
6652
6847
  promiseChain = this._chainOrCallHooks(promiseChain, "postAction");
6653
6848
  return promiseChain;
6654
6849
  }
6655
- if (this.parent && this.parent.listenerCount(commandEvent)) {
6850
+ if (this.parent?.listenerCount(commandEvent)) {
6656
6851
  checkForUnknownOptions();
6657
6852
  this._processArguments();
6658
6853
  this.parent.emit(commandEvent, operands, unknown);
@@ -6714,24 +6909,31 @@ Expecting one of '${allowedValues.join("', '")}'`);
6714
6909
  cmd._checkForConflictingLocalOptions();
6715
6910
  });
6716
6911
  }
6717
- parseOptions(argv) {
6912
+ parseOptions(args) {
6718
6913
  const operands = [];
6719
6914
  const unknown = [];
6720
6915
  let dest = operands;
6721
- const args = argv.slice();
6722
6916
  function maybeOption(arg) {
6723
6917
  return arg.length > 1 && arg[0] === "-";
6724
6918
  }
6919
+ const negativeNumberArg = (arg) => {
6920
+ if (!/^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/.test(arg))
6921
+ return false;
6922
+ return !this._getCommandAndAncestors().some((cmd) => cmd.options.map((opt) => opt.short).some((short) => /^-\d$/.test(short)));
6923
+ };
6725
6924
  let activeVariadicOption = null;
6726
- while (args.length) {
6727
- const arg = args.shift();
6925
+ let activeGroup = null;
6926
+ let i = 0;
6927
+ while (i < args.length || activeGroup) {
6928
+ const arg = activeGroup ?? args[i++];
6929
+ activeGroup = null;
6728
6930
  if (arg === "--") {
6729
6931
  if (dest === unknown)
6730
6932
  dest.push(arg);
6731
- dest.push(...args);
6933
+ dest.push(...args.slice(i));
6732
6934
  break;
6733
6935
  }
6734
- if (activeVariadicOption && !maybeOption(arg)) {
6936
+ if (activeVariadicOption && (!maybeOption(arg) || negativeNumberArg(arg))) {
6735
6937
  this.emit(`option:${activeVariadicOption.name()}`, arg);
6736
6938
  continue;
6737
6939
  }
@@ -6740,14 +6942,14 @@ Expecting one of '${allowedValues.join("', '")}'`);
6740
6942
  const option = this._findOption(arg);
6741
6943
  if (option) {
6742
6944
  if (option.required) {
6743
- const value = args.shift();
6945
+ const value = args[i++];
6744
6946
  if (value === undefined)
6745
6947
  this.optionMissingArgument(option);
6746
6948
  this.emit(`option:${option.name()}`, value);
6747
6949
  } else if (option.optional) {
6748
6950
  let value = null;
6749
- if (args.length > 0 && !maybeOption(args[0])) {
6750
- value = args.shift();
6951
+ if (i < args.length && (!maybeOption(args[i]) || negativeNumberArg(args[i]))) {
6952
+ value = args[i++];
6751
6953
  }
6752
6954
  this.emit(`option:${option.name()}`, value);
6753
6955
  } else {
@@ -6764,7 +6966,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
6764
6966
  this.emit(`option:${option.name()}`, arg.slice(2));
6765
6967
  } else {
6766
6968
  this.emit(`option:${option.name()}`);
6767
- args.unshift(`-${arg.slice(2)}`);
6969
+ activeGroup = `-${arg.slice(2)}`;
6768
6970
  }
6769
6971
  continue;
6770
6972
  }
@@ -6777,31 +6979,24 @@ Expecting one of '${allowedValues.join("', '")}'`);
6777
6979
  continue;
6778
6980
  }
6779
6981
  }
6780
- if (maybeOption(arg)) {
6982
+ if (dest === operands && maybeOption(arg) && !(this.commands.length === 0 && negativeNumberArg(arg))) {
6781
6983
  dest = unknown;
6782
6984
  }
6783
6985
  if ((this._enablePositionalOptions || this._passThroughOptions) && operands.length === 0 && unknown.length === 0) {
6784
6986
  if (this._findCommand(arg)) {
6785
6987
  operands.push(arg);
6786
- if (args.length > 0)
6787
- unknown.push(...args);
6988
+ unknown.push(...args.slice(i));
6788
6989
  break;
6789
6990
  } else if (this._getHelpCommand() && arg === this._getHelpCommand().name()) {
6790
- operands.push(arg);
6791
- if (args.length > 0)
6792
- operands.push(...args);
6991
+ operands.push(arg, ...args.slice(i));
6793
6992
  break;
6794
6993
  } else if (this._defaultCommandName) {
6795
- unknown.push(arg);
6796
- if (args.length > 0)
6797
- unknown.push(...args);
6994
+ unknown.push(arg, ...args.slice(i));
6798
6995
  break;
6799
6996
  }
6800
6997
  }
6801
6998
  if (this._passThroughOptions) {
6802
- dest.push(arg);
6803
- if (args.length > 0)
6804
- dest.push(...args);
6999
+ dest.push(arg, ...args.slice(i));
6805
7000
  break;
6806
7001
  }
6807
7002
  dest.push(arg);
@@ -7012,6 +7207,32 @@ Expecting one of '${allowedValues.join("', '")}'`);
7012
7207
  this._name = str;
7013
7208
  return this;
7014
7209
  }
7210
+ helpGroup(heading) {
7211
+ if (heading === undefined)
7212
+ return this._helpGroupHeading ?? "";
7213
+ this._helpGroupHeading = heading;
7214
+ return this;
7215
+ }
7216
+ commandsGroup(heading) {
7217
+ if (heading === undefined)
7218
+ return this._defaultCommandGroup ?? "";
7219
+ this._defaultCommandGroup = heading;
7220
+ return this;
7221
+ }
7222
+ optionsGroup(heading) {
7223
+ if (heading === undefined)
7224
+ return this._defaultOptionGroup ?? "";
7225
+ this._defaultOptionGroup = heading;
7226
+ return this;
7227
+ }
7228
+ _initOptionGroup(option) {
7229
+ if (this._defaultOptionGroup && !option.helpGroupHeading)
7230
+ option.helpGroup(this._defaultOptionGroup);
7231
+ }
7232
+ _initCommandGroup(cmd) {
7233
+ if (this._defaultCommandGroup && !cmd.helpGroup())
7234
+ cmd.helpGroup(this._defaultCommandGroup);
7235
+ }
7015
7236
  nameFromFilename(filename) {
7016
7237
  this._name = path.basename(filename, path.extname(filename));
7017
7238
  return this;
@@ -7024,23 +7245,38 @@ Expecting one of '${allowedValues.join("', '")}'`);
7024
7245
  }
7025
7246
  helpInformation(contextOptions) {
7026
7247
  const helper = this.createHelp();
7027
- if (helper.helpWidth === undefined) {
7028
- helper.helpWidth = contextOptions && contextOptions.error ? this._outputConfiguration.getErrHelpWidth() : this._outputConfiguration.getOutHelpWidth();
7029
- }
7030
- return helper.formatHelp(this, helper);
7248
+ const context = this._getOutputContext(contextOptions);
7249
+ helper.prepareContext({
7250
+ error: context.error,
7251
+ helpWidth: context.helpWidth,
7252
+ outputHasColors: context.hasColors
7253
+ });
7254
+ const text = helper.formatHelp(this, helper);
7255
+ if (context.hasColors)
7256
+ return text;
7257
+ return this._outputConfiguration.stripColor(text);
7031
7258
  }
7032
- _getHelpContext(contextOptions) {
7259
+ _getOutputContext(contextOptions) {
7033
7260
  contextOptions = contextOptions || {};
7034
- const context = { error: !!contextOptions.error };
7035
- let write;
7036
- if (context.error) {
7037
- write = (arg) => this._outputConfiguration.writeErr(arg);
7261
+ const error = !!contextOptions.error;
7262
+ let baseWrite;
7263
+ let hasColors;
7264
+ let helpWidth;
7265
+ if (error) {
7266
+ baseWrite = (str) => this._outputConfiguration.writeErr(str);
7267
+ hasColors = this._outputConfiguration.getErrHasColors();
7268
+ helpWidth = this._outputConfiguration.getErrHelpWidth();
7038
7269
  } else {
7039
- write = (arg) => this._outputConfiguration.writeOut(arg);
7040
- }
7041
- context.write = contextOptions.write || write;
7042
- context.command = this;
7043
- return context;
7270
+ baseWrite = (str) => this._outputConfiguration.writeOut(str);
7271
+ hasColors = this._outputConfiguration.getOutHasColors();
7272
+ helpWidth = this._outputConfiguration.getOutHelpWidth();
7273
+ }
7274
+ const write = (str) => {
7275
+ if (!hasColors)
7276
+ str = this._outputConfiguration.stripColor(str);
7277
+ return baseWrite(str);
7278
+ };
7279
+ return { error, write, hasColors, helpWidth };
7044
7280
  }
7045
7281
  outputHelp(contextOptions) {
7046
7282
  let deprecatedCallback;
@@ -7048,35 +7284,44 @@ Expecting one of '${allowedValues.join("', '")}'`);
7048
7284
  deprecatedCallback = contextOptions;
7049
7285
  contextOptions = undefined;
7050
7286
  }
7051
- const context = this._getHelpContext(contextOptions);
7052
- this._getCommandAndAncestors().reverse().forEach((command) => command.emit("beforeAllHelp", context));
7053
- this.emit("beforeHelp", context);
7054
- let helpInformation = this.helpInformation(context);
7287
+ const outputContext = this._getOutputContext(contextOptions);
7288
+ const eventContext = {
7289
+ error: outputContext.error,
7290
+ write: outputContext.write,
7291
+ command: this
7292
+ };
7293
+ this._getCommandAndAncestors().reverse().forEach((command) => command.emit("beforeAllHelp", eventContext));
7294
+ this.emit("beforeHelp", eventContext);
7295
+ let helpInformation = this.helpInformation({ error: outputContext.error });
7055
7296
  if (deprecatedCallback) {
7056
7297
  helpInformation = deprecatedCallback(helpInformation);
7057
7298
  if (typeof helpInformation !== "string" && !Buffer.isBuffer(helpInformation)) {
7058
7299
  throw new Error("outputHelp callback must return a string or a Buffer");
7059
7300
  }
7060
7301
  }
7061
- context.write(helpInformation);
7302
+ outputContext.write(helpInformation);
7062
7303
  if (this._getHelpOption()?.long) {
7063
7304
  this.emit(this._getHelpOption().long);
7064
7305
  }
7065
- this.emit("afterHelp", context);
7066
- this._getCommandAndAncestors().forEach((command) => command.emit("afterAllHelp", context));
7306
+ this.emit("afterHelp", eventContext);
7307
+ this._getCommandAndAncestors().forEach((command) => command.emit("afterAllHelp", eventContext));
7067
7308
  }
7068
7309
  helpOption(flags, description) {
7069
7310
  if (typeof flags === "boolean") {
7070
7311
  if (flags) {
7071
- this._helpOption = this._helpOption ?? undefined;
7312
+ if (this._helpOption === null)
7313
+ this._helpOption = undefined;
7314
+ if (this._defaultOptionGroup) {
7315
+ this._initOptionGroup(this._getHelpOption());
7316
+ }
7072
7317
  } else {
7073
7318
  this._helpOption = null;
7074
7319
  }
7075
7320
  return this;
7076
7321
  }
7077
- flags = flags ?? "-h, --help";
7078
- description = description ?? "display help for command";
7079
- this._helpOption = this.createOption(flags, description);
7322
+ this._helpOption = this.createOption(flags ?? "-h, --help", description ?? "display help for command");
7323
+ if (flags || description)
7324
+ this._initOptionGroup(this._helpOption);
7080
7325
  return this;
7081
7326
  }
7082
7327
  _getHelpOption() {
@@ -7087,11 +7332,12 @@ Expecting one of '${allowedValues.join("', '")}'`);
7087
7332
  }
7088
7333
  addHelpOption(option) {
7089
7334
  this._helpOption = option;
7335
+ this._initOptionGroup(option);
7090
7336
  return this;
7091
7337
  }
7092
7338
  help(contextOptions) {
7093
7339
  this.outputHelp(contextOptions);
7094
- let exitCode = process2.exitCode || 0;
7340
+ let exitCode = Number(process2.exitCode ?? 0);
7095
7341
  if (exitCode === 0 && contextOptions && typeof contextOptions !== "function" && contextOptions.error) {
7096
7342
  exitCode = 1;
7097
7343
  }
@@ -7156,10 +7402,18 @@ Expecting one of '${allowedValues.join("', '")}'`);
7156
7402
  return arg;
7157
7403
  });
7158
7404
  }
7405
+ function useColor() {
7406
+ if (process2.env.NO_COLOR || process2.env.FORCE_COLOR === "0" || process2.env.FORCE_COLOR === "false")
7407
+ return false;
7408
+ if (process2.env.FORCE_COLOR || process2.env.CLICOLOR_FORCE !== undefined)
7409
+ return true;
7410
+ return;
7411
+ }
7159
7412
  exports.Command = Command;
7413
+ exports.useColor = useColor;
7160
7414
  });
7161
7415
 
7162
- // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/index.js
7416
+ // ../../node_modules/.bun/commander@14.0.3/node_modules/commander/index.js
7163
7417
  var require_commander = __commonJS((exports) => {
7164
7418
  var { Argument } = require_argument();
7165
7419
  var { Command } = require_command();
@@ -7184,7 +7438,7 @@ var exports_meta = {};
7184
7438
  __export(exports_meta, {
7185
7439
  PKG_VERSION: () => PKG_VERSION
7186
7440
  });
7187
- var PKG_VERSION = "1.0.5";
7441
+ var PKG_VERSION = "1.1.0-beta.1";
7188
7442
  var init_meta = () => {};
7189
7443
 
7190
7444
  // ../../node_modules/.bun/tslib@2.8.1/node_modules/tslib/tslib.js
@@ -25217,21 +25471,46 @@ function getMaxResponseBytes() {
25217
25471
  const n = Number.parseInt(raw, 10);
25218
25472
  return Number.isNaN(n) || n <= 0 ? MAX_RESPONSE_BYTES : n;
25219
25473
  }
25220
- function getMinSearchScore() {
25221
- const raw = globalThis.process?.env?.CEREFOX_MIN_SEARCH_SCORE;
25222
- const fallback = globalThis.process?.env?.CEREFOX_EMBEDDER === "local" ? DEFAULT_MIN_SEARCH_SCORE_LOCAL : DEFAULT_MIN_SEARCH_SCORE;
25223
- if (raw === undefined || raw === "")
25224
- return fallback;
25225
- const n = Number.parseFloat(raw);
25226
- return Number.isNaN(n) || n < 0 || n > 1 ? fallback : n;
25474
+ function readEnv(name) {
25475
+ const g = globalThis;
25476
+ const fromProcess = g.process?.env?.[name];
25477
+ if (fromProcess !== undefined && fromProcess !== "")
25478
+ return fromProcess;
25479
+ try {
25480
+ const fromDeno = g.Deno?.env?.get(name);
25481
+ return fromDeno === "" ? undefined : fromDeno;
25482
+ } catch {
25483
+ return;
25484
+ }
25227
25485
  }
25228
- function getMinTermCoverage() {
25229
- const raw = globalThis.process?.env?.CEREFOX_MIN_TERM_COVERAGE;
25230
- if (raw === undefined || raw === "")
25486
+ function readUnitInterval(name) {
25487
+ const raw = readEnv(name);
25488
+ if (raw === undefined)
25231
25489
  return;
25232
25490
  const n = Number.parseFloat(raw);
25233
25491
  return Number.isNaN(n) || n < 0 || n > 1 ? undefined : n;
25234
25492
  }
25493
+ function getSearchAlpha() {
25494
+ return readUnitInterval("CEREFOX_SEARCH_ALPHA") ?? DEFAULT_SEARCH_ALPHA;
25495
+ }
25496
+ function getConfiguredMinSearchScore() {
25497
+ const explicit = readUnitInterval("CEREFOX_MIN_SEARCH_SCORE");
25498
+ if (explicit !== undefined)
25499
+ return explicit;
25500
+ if (readEnv("CEREFOX_EMBEDDER") === "local")
25501
+ return DEFAULT_MIN_SEARCH_SCORE_LOCAL;
25502
+ return;
25503
+ }
25504
+ function getConfiguredSearchAlpha() {
25505
+ return readUnitInterval("CEREFOX_SEARCH_ALPHA");
25506
+ }
25507
+ function getMinSearchScore() {
25508
+ const fallback = readEnv("CEREFOX_EMBEDDER") === "local" ? DEFAULT_MIN_SEARCH_SCORE_LOCAL : DEFAULT_MIN_SEARCH_SCORE;
25509
+ return readUnitInterval("CEREFOX_MIN_SEARCH_SCORE") ?? fallback;
25510
+ }
25511
+ function getMinTermCoverage() {
25512
+ return readUnitInterval("CEREFOX_MIN_TERM_COVERAGE");
25513
+ }
25235
25514
  function applyByteBudget(rows, maxBytes) {
25236
25515
  const accepted = [];
25237
25516
  let usedBytes = 0;
@@ -25259,7 +25538,7 @@ function logUsage(supabase, params) {
25259
25538
  p_extra: params.extra ?? {}
25260
25539
  })).catch(() => {});
25261
25540
  }
25262
- var MAX_RESPONSE_BYTES = 200000, DEFAULT_MIN_SEARCH_SCORE = 0.5, DEFAULT_MIN_SEARCH_SCORE_LOCAL = 0.6;
25541
+ var MAX_RESPONSE_BYTES = 200000, DEFAULT_MIN_SEARCH_SCORE = 0.5, DEFAULT_MIN_SEARCH_SCORE_LOCAL = 0.6, DEFAULT_SEARCH_ALPHA = 0.7;
25263
25542
 
25264
25543
  // ../../_shared/mcp-tools/_projects.ts
25265
25544
  async function ensureDocumentInProject(supabase, documentId, projectName) {
@@ -54918,6 +55197,204 @@ var init_types3 = __esm(() => {
54918
55197
  };
54919
55198
  });
54920
55199
 
55200
+ // ../../_shared/mcp-tools/relations.ts
55201
+ function requireUuid2(value, field) {
55202
+ const s = typeof value === "string" ? value.trim() : "";
55203
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s)) {
55204
+ throw new McpInvalidParams(`${field} must be a document UUID (got: ${String(value)})`);
55205
+ }
55206
+ return s;
55207
+ }
55208
+ async function setHandler(supabase, args, ctx) {
55209
+ const source = requireUuid2(args.source_id, "source_id");
55210
+ const target = requireUuid2(args.target_id, "target_id");
55211
+ const relType = typeof args.rel_type === "string" ? args.rel_type.trim() : "";
55212
+ if (!relType)
55213
+ throw new McpInvalidParams("rel_type is required");
55214
+ const { data, error: error2 } = await supabase.rpc("cerefox_set_relation", {
55215
+ p_source_id: source,
55216
+ p_target_id: target,
55217
+ p_rel_type: relType,
55218
+ p_author: args.author ?? "mcp-agent",
55219
+ p_author_type: "agent",
55220
+ p_metadata: args.metadata ?? {}
55221
+ });
55222
+ if (error2)
55223
+ throw new Error(`RPC error: ${error2.message}`);
55224
+ const row = Array.isArray(data) ? data[0] : data;
55225
+ logUsage(supabase, {
55226
+ operation: "set_relation",
55227
+ accessPath: ctx.accessPath,
55228
+ requestor: args.requestor,
55229
+ document_id: source
55230
+ });
55231
+ const both = row?.is_symmetric ? " (symmetric — the reverse edge was written too)" : "";
55232
+ const effect = relType === "supersedes" ? `
55233
+ Target marked superseded.` : relType === "contradicts" ? `
55234
+ Both documents marked stale.` : "";
55235
+ return `Relation set: ${source} --${relType}--> ${target}${both}.${effect}`;
55236
+ }
55237
+ async function deleteHandler(supabase, args, ctx) {
55238
+ const source = requireUuid2(args.source_id, "source_id");
55239
+ const target = requireUuid2(args.target_id, "target_id");
55240
+ const relType = typeof args.rel_type === "string" ? args.rel_type.trim() : "";
55241
+ if (!relType)
55242
+ throw new McpInvalidParams("rel_type is required");
55243
+ const { data, error: error2 } = await supabase.rpc("cerefox_delete_relation", {
55244
+ p_source_id: source,
55245
+ p_target_id: target,
55246
+ p_rel_type: relType,
55247
+ p_author: args.author ?? "mcp-agent",
55248
+ p_author_type: "agent"
55249
+ });
55250
+ if (error2)
55251
+ throw new Error(`RPC error: ${error2.message}`);
55252
+ const removed = typeof data === "number" ? data : 0;
55253
+ logUsage(supabase, {
55254
+ operation: "delete_relation",
55255
+ accessPath: ctx.accessPath,
55256
+ requestor: args.requestor,
55257
+ document_id: source
55258
+ });
55259
+ if (removed === 0)
55260
+ return "No such relation — nothing removed.";
55261
+ return `Removed ${removed} relation row(s): ${source} --${relType}--> ${target}. ` + "Lifecycle status is left as-is (a document may be superseded by something else too).";
55262
+ }
55263
+ async function getRelationsHandler(supabase, args, ctx) {
55264
+ const docId = requireUuid2(args.document_id, "document_id");
55265
+ const { data, error: error2 } = await supabase.rpc("cerefox_get_relations", {
55266
+ p_document_id: docId
55267
+ });
55268
+ if (error2)
55269
+ throw new Error(`RPC error: ${error2.message}`);
55270
+ const rows = data ?? [];
55271
+ logUsage(supabase, {
55272
+ operation: "get_relations",
55273
+ accessPath: ctx.accessPath,
55274
+ requestor: args.requestor,
55275
+ document_id: docId,
55276
+ result_count: rows.length
55277
+ });
55278
+ if (rows.length === 0)
55279
+ return "No relations for this document.";
55280
+ const lines = rows.map((r) => {
55281
+ const arrow = r.direction === "outbound" ? "→" : "←";
55282
+ const life = r.other_lifecycle && r.other_lifecycle !== "active" ? ` [${r.other_lifecycle}]` : "";
55283
+ return `- ${arrow} ${r.rel_type}: ${r.other_title}${life} [id: ${r.other_id}]`;
55284
+ });
55285
+ return `${rows.length} relation(s):
55286
+ ${lines.join(`
55287
+ `)}`;
55288
+ }
55289
+ async function getNeighborsHandler(supabase, args, ctx) {
55290
+ const docId = requireUuid2(args.document_id, "document_id");
55291
+ const relType = typeof args.rel_type === "string" ? args.rel_type.trim() : "";
55292
+ if (!relType)
55293
+ throw new McpInvalidParams("rel_type is required — pick one type to traverse");
55294
+ const depth = Math.max(1, Math.min(args.depth ?? 1, 5));
55295
+ const { data, error: error2 } = await supabase.rpc("cerefox_get_neighbors", {
55296
+ p_document_id: docId,
55297
+ p_rel_type: relType,
55298
+ p_depth: depth,
55299
+ p_from_time: args.from_time ?? null,
55300
+ p_to_time: args.to_time ?? null,
55301
+ p_limit: Math.max(1, Math.min(args.limit ?? 50, 200))
55302
+ });
55303
+ if (error2)
55304
+ throw new Error(`RPC error: ${error2.message}`);
55305
+ const rows = data ?? [];
55306
+ logUsage(supabase, {
55307
+ operation: "get_neighbors",
55308
+ accessPath: ctx.accessPath,
55309
+ requestor: args.requestor,
55310
+ document_id: docId,
55311
+ result_count: rows.length
55312
+ });
55313
+ if (rows.length === 0)
55314
+ return `No documents reachable via "${relType}".`;
55315
+ const lines = rows.map((r) => {
55316
+ const life = r.lifecycle_status && r.lifecycle_status !== "active" ? ` [${r.lifecycle_status}]` : "";
55317
+ return `- depth ${r.depth} (${r.direction}): ${r.title}${life} [id: ${r.document_id}]`;
55318
+ });
55319
+ return `${rows.length} document(s) via "${relType}":
55320
+ ${lines.join(`
55321
+ `)}`;
55322
+ }
55323
+ var KNOWN_TYPES = "related_to, references, supersedes, contradicts, duplicates, part_of, follows, reply_to", setRelationTool, deleteRelationTool, getRelationsTool, getNeighborsTool;
55324
+ var init_relations = __esm(() => {
55325
+ init_types3();
55326
+ setRelationTool = {
55327
+ name: "cerefox_set_relation",
55328
+ description: "Link two documents with a typed, directed relation (source → target). " + `Known types with behaviour: ${KNOWN_TYPES}. Symmetric types (related_to, ` + "contradicts, duplicates) write both directions. `supersedes` marks the target " + "superseded; `contradicts` marks both stale. Any other type string is accepted " + "and stored, just without special behaviour. Re-setting the same edge updates it.",
55329
+ inputSchema: {
55330
+ type: "object",
55331
+ required: ["source_id", "target_id", "rel_type"],
55332
+ properties: {
55333
+ source_id: { type: "string", description: "UUID of the source document" },
55334
+ target_id: { type: "string", description: "UUID of the target document" },
55335
+ rel_type: {
55336
+ type: "string",
55337
+ description: `Relation type, e.g. one of: ${KNOWN_TYPES}. Free text is allowed.`
55338
+ },
55339
+ metadata: {
55340
+ type: "object",
55341
+ description: "Optional JSON context for the edge (note, confidence, …)"
55342
+ },
55343
+ author: { type: "string", description: "Who is creating this relation" },
55344
+ requestor: { type: "string", description: "Name of the agent making this request" }
55345
+ }
55346
+ },
55347
+ handler: setHandler
55348
+ };
55349
+ deleteRelationTool = {
55350
+ name: "cerefox_delete_relation",
55351
+ description: "Remove a typed relation between two documents. Symmetric types remove both " + "directions. Lifecycle status set by an earlier relation is NOT reverted.",
55352
+ inputSchema: {
55353
+ type: "object",
55354
+ required: ["source_id", "target_id", "rel_type"],
55355
+ properties: {
55356
+ source_id: { type: "string", description: "UUID of the source document" },
55357
+ target_id: { type: "string", description: "UUID of the target document" },
55358
+ rel_type: { type: "string", description: "Relation type to remove" },
55359
+ author: { type: "string", description: "Who is removing this relation" },
55360
+ requestor: { type: "string", description: "Name of the agent making this request" }
55361
+ }
55362
+ },
55363
+ handler: deleteHandler
55364
+ };
55365
+ getRelationsTool = {
55366
+ name: "cerefox_get_relations",
55367
+ description: "List every relation touching a document, in both directions (→ outbound, " + "← inbound). Shows each neighbour's title and lifecycle status, so an agent " + "can tell whether retrieved knowledge has been superseded or contradicted.",
55368
+ inputSchema: {
55369
+ type: "object",
55370
+ required: ["document_id"],
55371
+ properties: {
55372
+ document_id: { type: "string", description: "UUID of the document" },
55373
+ requestor: { type: "string", description: "Name of the agent making this request" }
55374
+ }
55375
+ },
55376
+ handler: getRelationsHandler
55377
+ };
55378
+ getNeighborsTool = {
55379
+ name: "cerefox_get_neighbors",
55380
+ description: "Walk the relation graph outward from a document along ONE relation type. " + "Use after cerefox_get_relations shows which types exist. depth > 1 follows " + "chains (useful for follows / reply_to); cycles terminate safely. Optional " + "from_time / to_time filter neighbours by their creation time.",
55381
+ inputSchema: {
55382
+ type: "object",
55383
+ required: ["document_id", "rel_type"],
55384
+ properties: {
55385
+ document_id: { type: "string", description: "UUID of the starting document" },
55386
+ rel_type: { type: "string", description: "The single relation type to traverse" },
55387
+ depth: { type: "integer", description: "How many hops to follow (1–5, default 1)" },
55388
+ from_time: { type: "string", description: "ISO-8601: only neighbours created on/after" },
55389
+ to_time: { type: "string", description: "ISO-8601: only neighbours created on/before" },
55390
+ limit: { type: "integer", description: "Max documents to return (default 50, max 200)" },
55391
+ requestor: { type: "string", description: "Name of the agent making this request" }
55392
+ }
55393
+ },
55394
+ handler: getNeighborsHandler
55395
+ };
55396
+ });
55397
+
54921
55398
  // ../../_shared/mcp-tools/get-document.ts
54922
55399
  async function handler2(supabase, args, ctx) {
54923
55400
  const document_id = args.document_id;
@@ -54973,11 +55450,11 @@ var init_get_document = __esm(() => {
54973
55450
  });
54974
55451
 
54975
55452
  // ../../_shared/mcp-tools/get-help-content.ts
54976
- var HELP_FULL = '# Cerefox Knowledge Base -- Agent Quick Reference\n\nCerefox is a persistent, shared knowledge base. You have **10 MCP tools** (9 of them have CLI equivalents — `cerefox_get_help` is MCP-only). For the full guide, search Cerefox for "How AI Agents Use Cerefox" or call `cerefox_get_help` to retrieve this content over MCP.\n\n## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `requestor` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `expected_content_hash` (**required on content updates** — see rule 9), `last_write_wins`, `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata` (omit on update to keep existing tags; `{}` clears), `author` |\n| `cerefox_get_document` | Get full document by ID (header includes `content_hash` — the update token) | `document_id` (required) |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required) |\n| `cerefox_metadata_search` | Find or list docs by metadata, project, or time (no text query) | `metadata_filter`, `project_name` (list a project\'s docs), `updated_since`, `include_content` — **at least one** of metadata_filter/project_name/updated_since/created_since |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `cerefox_set_document_projects` | Set doc\'s project memberships to exactly the given list (destructive replace; metadata-only, no content change) | `document_id`, `project_names` (required) |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `author`, `operation`, `since` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |\n\n## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author`/`requestor`** to your name on every call (e.g., "Claude Code", "archiver"). On MCP, pass as parameters. On CLI, pass `--author`/`--author-type`/`--requestor` flags, or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE`/`CEREFOX_REQUESTOR_NAME` env vars set in the user\'s `.env`.\n4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.\n5. **Add metadata** -- at minimum `type` ("decision-log", "research", "design-doc") and `status` ("active", "draft").\n6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.\n7. **Deletes are soft (recoverable); purge is web-UI-only.** If you decide to delete, surface it to the user (`I soft-deleted X — recoverable from the Cerefox web UI trash`). You cannot un-do your own delete from agent code by design.\n8. **Cross-doc links inside content**: **always use `[Text](document-uuid)`.** UUIDs are the only fully reliable link form — stable across title changes, never ambiguous, no encoding gotchas. Every `cerefox_search` result shows `[id: <uuid>]` after the title; grab it and use it. Title-based linking (`[Text](<Title With Spaces>)`) is fragile (breaks on colons, parens, ampersands, brackets — silently navigates to wrong page) — **don\'t write title-based links**; do an extra search to get the UUID instead. Repo-path forms (`[Text](docs/path.md)`) exist for repo-ingested files; don\'t construct manually. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.\n9. **Concurrency: content updates require `expected_content_hash`.** Pass the `content_hash` you read (shown by `cerefox_get_document`, `cerefox_search`, and `cerefox_metadata_search`) when updating a document. If it\'s stale you get a **conflict** — re-read the document, merge your changes into the latest content, retry with the new hash. **Never resolve a conflict by overwriting blindly** — the current content includes another writer\'s work. `last_write_wins: true` skips the check; use it ONLY when an external source of truth makes conflicts meaningless (file re-sync), never to silence a conflict.\n10. **Search: prefer a few distinctive terms; heed `below confidence`.** When nothing clears the relevance threshold, `cerefox_search` returns the closest candidates prefixed with a `below confidence` warning instead of an empty set — that flag means **weak signal, not absent knowledge**: check the candidates\' scores and titles before concluding the KB lacks the content. A truly empty response means nothing even weakly related exists.\n11. **Project memberships — non-destructive by default**: on `cerefox_ingest` updates, **`project_name` (singular) is a non-destructive add** (ensures membership, preserves others). Use **`project_names` (list)** when you want to set the doc\'s full project set in one call (destructive replace). For metadata-only project changes without writing content, use **`cerefox_set_document_projects(document_id, project_names)`** — that tool is the destructive-replace contract made explicit. Never call `cerefox_set_document_projects` with a single name when you mean "add" — that would REMOVE the doc from all other projects. When in doubt, use `cerefox_ingest` with singular `project_name`.\n\n## Update Workflow (ID-based -- preferred)\n\n```\nsearch("topic") -> find doc [id: abc123] -> get_document(abc123) -> note its content_hash -> modify ->\ningest(title="Same Title", content="...", document_id="abc123",\n expected_content_hash="<the hash you read>", author="my-agent")\n```\n\nOn a **conflict** error: get_document again (fresh content + fresh hash) -> merge your changes -> retry with the new hash.\n\n## Update Workflow (title-based -- fallback)\n\n```\nsearch("topic") -> find doc (note its hash) -> modify ->\ningest(title="Same Title", content="...", update_if_exists=true,\n expected_content_hash="<the hash you read>", author="my-agent")\n```\n\n## Catch-Up Workflow\n\n```\nmetadata_search(metadata_filter={"type": "decision-log"}, updated_since="2026-03-28T00:00:00Z")\n```\n\n## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. The canonical invocation is plain **`cerefox <subcommand>`** (the TypeScript CLI, installed via `npm install -g @cerefox/memory`). It uses a resource-verb shape (`cerefox document get`, `cerefox project list`, …).\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); common flags also have single-letter short forms (`-f`, `-p`, `-c`, `-m`, `-u`, `-a`, `-r`). Use the canonical long name (what `--help` shows) or its short form — there are no long-form aliases like `--filter` or `--count`.\n\n| MCP tool | CLI |\n|---|---|\n| `cerefox_search` | `cerefox search "<q>" --requestor "<your-name>"` |\n| `cerefox_ingest` (paste) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --author "<your-name>" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --document-id "<uuid>" --expected-content-hash "<hash>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --requestor "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --requestor "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --requestor "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --requestor "<your-name>"` (list a project: `cerefox document list --project <name>`) |\n| `cerefox_set_document_projects` | `cerefox document set-projects <id> <name...> --author "<your-name>" --author-type agent` (or `--clear` to remove all) |\n| `cerefox_get_audit_log` | `cerefox audit list --requestor "<your-name>"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox guides show agent-quick-reference` (or `cerefox guides list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`document ingest`, `document ingest-dir`): `--author "<your-name>" --author-type agent`\n- Reads: `--requestor "<your-name>"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.\n', HELP_SECTIONS, HELP_SECTION_HEADINGS;
55453
+ var HELP_FULL = '# Cerefox Knowledge Base -- Agent Quick Reference\n\nCerefox is a persistent, shared knowledge base. You have **14 MCP tools** (13 of them have CLI equivalents — `cerefox_get_help` is MCP-only). For the full guide, search Cerefox for "How AI Agents Use Cerefox" or call `cerefox_get_help` to retrieve this content over MCP.\n\n## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `requestor` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `expected_content_hash` (**required on content updates** — see rule 9), `last_write_wins`, `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata` (omit on update to keep existing tags; `{}` clears), `author` |\n| `cerefox_get_document` | Get full document by ID (header includes `content_hash` — the update token) | `document_id` (required) |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required) |\n| `cerefox_set_relation` | Link two documents (`source --rel_type--> target`) | `source_id`, `target_id`, `rel_type` (required), `metadata`, `author` |\n| `cerefox_delete_relation` | Remove a relation | `source_id`, `target_id`, `rel_type` |\n| `cerefox_get_relations` | All relations touching a document, both directions | `document_id` |\n| `cerefox_get_neighbors` | Walk the graph along ONE relation type | `document_id`, `rel_type` (required), `depth`, `from_time`, `to_time`, `limit` |\n| `cerefox_metadata_search` | Find or list docs by metadata, project, or time (no text query) | `metadata_filter`, `project_name` (list a project\'s docs), `updated_since`, `include_content` — **at least one** of metadata_filter/project_name/updated_since/created_since |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `cerefox_set_document_projects` | Set doc\'s project memberships to exactly the given list (destructive replace; metadata-only, no content change) | `document_id`, `project_names` (required) |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `author`, `operation`, `since` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |\n\n## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author`/`requestor`** to your name on every call (e.g., "Claude Code", "archiver"). On MCP, pass as parameters. On CLI, pass `--author`/`--author-type`/`--requestor` flags, or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE`/`CEREFOX_REQUESTOR_NAME` env vars set in the user\'s `.env`.\n4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.\n5. **Add metadata** -- at minimum `type` ("decision-log", "research", "design-doc") and `status` ("active", "draft").\n6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.\n7. **Deletes are soft (recoverable); purge is web-UI-only.** If you decide to delete, surface it to the user (`I soft-deleted X — recoverable from the Cerefox web UI trash`). You cannot un-do your own delete from agent code by design.\n8. **Cross-doc links inside content**: **always use `[Text](document-uuid)`.** UUIDs are the only fully reliable link form — stable across title changes, never ambiguous, no encoding gotchas. Every `cerefox_search` result shows `[id: <uuid>]` after the title; grab it and use it. Title-based linking (`[Text](<Title With Spaces>)`) is fragile (breaks on colons, parens, ampersands, brackets — silently navigates to wrong page) — **don\'t write title-based links**; do an extra search to get the UUID instead. Repo-path forms (`[Text](docs/path.md)`) exist for repo-ingested files; don\'t construct manually. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.\n9. **Concurrency: content updates require `expected_content_hash`.** Pass the `content_hash` you read (shown by `cerefox_get_document`, `cerefox_search`, and `cerefox_metadata_search`) when updating a document. If it\'s stale you get a **conflict** — re-read the document, merge your changes into the latest content, retry with the new hash. **Never resolve a conflict by overwriting blindly** — the current content includes another writer\'s work. `last_write_wins: true` skips the check; use it ONLY when an external source of truth makes conflicts meaningless (file re-sync), never to silence a conflict.\n10. **Search: prefer a few distinctive terms; heed `below confidence`.** When nothing clears the relevance threshold, `cerefox_search` returns the closest candidates prefixed with a `below confidence` warning instead of an empty set — that flag means **weak signal, not absent knowledge**: check the candidates\' scores and titles before concluding the KB lacks the content. A truly empty response means nothing even weakly related exists.\n11. **Relations express how documents relate; lifecycle tells you if knowledge is still good.** Use `cerefox_set_relation` when one document supersedes, contradicts, references, or continues another. `supersedes` marks the target **superseded**; `contradicts` marks **both** stale; `related_to`/`duplicates`/`contradicts` are symmetric (both directions written). Any other type string is accepted without special behaviour. When a search result or `cerefox_get_relations` shows a neighbour marked `[superseded]` or `[stale]`, say so rather than presenting it as current.\n12. **Project memberships — non-destructive by default**: on `cerefox_ingest` updates, **`project_name` (singular) is a non-destructive add** (ensures membership, preserves others). Use **`project_names` (list)** when you want to set the doc\'s full project set in one call (destructive replace). For metadata-only project changes without writing content, use **`cerefox_set_document_projects(document_id, project_names)`** — that tool is the destructive-replace contract made explicit. Never call `cerefox_set_document_projects` with a single name when you mean "add" — that would REMOVE the doc from all other projects. When in doubt, use `cerefox_ingest` with singular `project_name`.\n\n## Update Workflow (ID-based -- preferred)\n\n```\nsearch("topic") -> find doc [id: abc123] -> get_document(abc123) -> note its content_hash -> modify ->\ningest(title="Same Title", content="...", document_id="abc123",\n expected_content_hash="<the hash you read>", author="my-agent")\n```\n\nOn a **conflict** error: get_document again (fresh content + fresh hash) -> merge your changes -> retry with the new hash.\n\n## Update Workflow (title-based -- fallback)\n\n```\nsearch("topic") -> find doc (note its hash) -> modify ->\ningest(title="Same Title", content="...", update_if_exists=true,\n expected_content_hash="<the hash you read>", author="my-agent")\n```\n\n## Catch-Up Workflow\n\n```\nmetadata_search(metadata_filter={"type": "decision-log"}, updated_since="2026-03-28T00:00:00Z")\n```\n\n## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. The canonical invocation is plain **`cerefox <subcommand>`** (the TypeScript CLI, installed via `npm install -g @cerefox/memory`). It uses a resource-verb shape (`cerefox document get`, `cerefox project list`, …).\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); common flags also have single-letter short forms (`-f`, `-p`, `-c`, `-m`, `-u`, `-a`, `-r`). Use the canonical long name (what `--help` shows) or its short form — there are no long-form aliases like `--filter` or `--count`.\n\n| MCP tool | CLI |\n|---|---|\n| `cerefox_search` | `cerefox search "<q>" --requestor "<your-name>"` |\n| `cerefox_ingest` (paste) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --author "<your-name>" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --document-id "<uuid>" --expected-content-hash "<hash>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --requestor "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --requestor "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --requestor "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_set_relation` | Link two documents (`source --rel_type--> target`) | `source_id`, `target_id`, `rel_type` (required), `metadata`, `author` |\n| `cerefox_delete_relation` | Remove a relation | `source_id`, `target_id`, `rel_type` |\n| `cerefox_get_relations` | All relations touching a document, both directions | `document_id` |\n| `cerefox_get_neighbors` | Walk the graph along ONE relation type | `document_id`, `rel_type` (required), `depth`, `from_time`, `to_time`, `limit` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --requestor "<your-name>"` (list a project: `cerefox document list --project <name>`) |\n| `cerefox_set_document_projects` | `cerefox document set-projects <id> <name...> --author "<your-name>" --author-type agent` (or `--clear` to remove all) |\n| `cerefox_get_audit_log` | `cerefox audit list --requestor "<your-name>"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox guides show agent-quick-reference` (or `cerefox guides list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`document ingest`, `document ingest-dir`): `--author "<your-name>" --author-type agent`\n- Reads: `--requestor "<your-name>"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.\n', HELP_SECTIONS, HELP_SECTION_HEADINGS;
54977
55454
  var init_get_help_content = __esm(() => {
54978
55455
  HELP_SECTIONS = {
54979
- Tools: "## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `requestor` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `expected_content_hash` (**required on content updates** — see rule 9), `last_write_wins`, `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata` (omit on update to keep existing tags; `{}` clears), `author` |\n| `cerefox_get_document` | Get full document by ID (header includes `content_hash` — the update token) | `document_id` (required) |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required) |\n| `cerefox_metadata_search` | Find or list docs by metadata, project, or time (no text query) | `metadata_filter`, `project_name` (list a project's docs), `updated_since`, `include_content` — **at least one** of metadata_filter/project_name/updated_since/created_since |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `cerefox_set_document_projects` | Set doc's project memberships to exactly the given list (destructive replace; metadata-only, no content change) | `document_id`, `project_names` (required) |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `author`, `operation`, `since` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |",
54980
- "Essential Rules": '## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author`/`requestor`** to your name on every call (e.g., "Claude Code", "archiver"). On MCP, pass as parameters. On CLI, pass `--author`/`--author-type`/`--requestor` flags, or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE`/`CEREFOX_REQUESTOR_NAME` env vars set in the user\'s `.env`.\n4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.\n5. **Add metadata** -- at minimum `type` ("decision-log", "research", "design-doc") and `status` ("active", "draft").\n6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.\n7. **Deletes are soft (recoverable); purge is web-UI-only.** If you decide to delete, surface it to the user (`I soft-deleted X — recoverable from the Cerefox web UI trash`). You cannot un-do your own delete from agent code by design.\n8. **Cross-doc links inside content**: **always use `[Text](document-uuid)`.** UUIDs are the only fully reliable link form — stable across title changes, never ambiguous, no encoding gotchas. Every `cerefox_search` result shows `[id: <uuid>]` after the title; grab it and use it. Title-based linking (`[Text](<Title With Spaces>)`) is fragile (breaks on colons, parens, ampersands, brackets — silently navigates to wrong page) — **don\'t write title-based links**; do an extra search to get the UUID instead. Repo-path forms (`[Text](docs/path.md)`) exist for repo-ingested files; don\'t construct manually. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.\n9. **Concurrency: content updates require `expected_content_hash`.** Pass the `content_hash` you read (shown by `cerefox_get_document`, `cerefox_search`, and `cerefox_metadata_search`) when updating a document. If it\'s stale you get a **conflict** — re-read the document, merge your changes into the latest content, retry with the new hash. **Never resolve a conflict by overwriting blindly** — the current content includes another writer\'s work. `last_write_wins: true` skips the check; use it ONLY when an external source of truth makes conflicts meaningless (file re-sync), never to silence a conflict.\n10. **Search: prefer a few distinctive terms; heed `below confidence`.** When nothing clears the relevance threshold, `cerefox_search` returns the closest candidates prefixed with a `below confidence` warning instead of an empty set — that flag means **weak signal, not absent knowledge**: check the candidates\' scores and titles before concluding the KB lacks the content. A truly empty response means nothing even weakly related exists.\n11. **Project memberships — non-destructive by default**: on `cerefox_ingest` updates, **`project_name` (singular) is a non-destructive add** (ensures membership, preserves others). Use **`project_names` (list)** when you want to set the doc\'s full project set in one call (destructive replace). For metadata-only project changes without writing content, use **`cerefox_set_document_projects(document_id, project_names)`** — that tool is the destructive-replace contract made explicit. Never call `cerefox_set_document_projects` with a single name when you mean "add" — that would REMOVE the doc from all other projects. When in doubt, use `cerefox_ingest` with singular `project_name`.',
55456
+ Tools: "## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `requestor` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `expected_content_hash` (**required on content updates** — see rule 9), `last_write_wins`, `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata` (omit on update to keep existing tags; `{}` clears), `author` |\n| `cerefox_get_document` | Get full document by ID (header includes `content_hash` — the update token) | `document_id` (required) |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required) |\n| `cerefox_set_relation` | Link two documents (`source --rel_type--> target`) | `source_id`, `target_id`, `rel_type` (required), `metadata`, `author` |\n| `cerefox_delete_relation` | Remove a relation | `source_id`, `target_id`, `rel_type` |\n| `cerefox_get_relations` | All relations touching a document, both directions | `document_id` |\n| `cerefox_get_neighbors` | Walk the graph along ONE relation type | `document_id`, `rel_type` (required), `depth`, `from_time`, `to_time`, `limit` |\n| `cerefox_metadata_search` | Find or list docs by metadata, project, or time (no text query) | `metadata_filter`, `project_name` (list a project's docs), `updated_since`, `include_content` — **at least one** of metadata_filter/project_name/updated_since/created_since |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `cerefox_set_document_projects` | Set doc's project memberships to exactly the given list (destructive replace; metadata-only, no content change) | `document_id`, `project_names` (required) |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `author`, `operation`, `since` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |",
55457
+ "Essential Rules": '## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author`/`requestor`** to your name on every call (e.g., "Claude Code", "archiver"). On MCP, pass as parameters. On CLI, pass `--author`/`--author-type`/`--requestor` flags, or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE`/`CEREFOX_REQUESTOR_NAME` env vars set in the user\'s `.env`.\n4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.\n5. **Add metadata** -- at minimum `type` ("decision-log", "research", "design-doc") and `status` ("active", "draft").\n6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.\n7. **Deletes are soft (recoverable); purge is web-UI-only.** If you decide to delete, surface it to the user (`I soft-deleted X — recoverable from the Cerefox web UI trash`). You cannot un-do your own delete from agent code by design.\n8. **Cross-doc links inside content**: **always use `[Text](document-uuid)`.** UUIDs are the only fully reliable link form — stable across title changes, never ambiguous, no encoding gotchas. Every `cerefox_search` result shows `[id: <uuid>]` after the title; grab it and use it. Title-based linking (`[Text](<Title With Spaces>)`) is fragile (breaks on colons, parens, ampersands, brackets — silently navigates to wrong page) — **don\'t write title-based links**; do an extra search to get the UUID instead. Repo-path forms (`[Text](docs/path.md)`) exist for repo-ingested files; don\'t construct manually. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.\n9. **Concurrency: content updates require `expected_content_hash`.** Pass the `content_hash` you read (shown by `cerefox_get_document`, `cerefox_search`, and `cerefox_metadata_search`) when updating a document. If it\'s stale you get a **conflict** — re-read the document, merge your changes into the latest content, retry with the new hash. **Never resolve a conflict by overwriting blindly** — the current content includes another writer\'s work. `last_write_wins: true` skips the check; use it ONLY when an external source of truth makes conflicts meaningless (file re-sync), never to silence a conflict.\n10. **Search: prefer a few distinctive terms; heed `below confidence`.** When nothing clears the relevance threshold, `cerefox_search` returns the closest candidates prefixed with a `below confidence` warning instead of an empty set — that flag means **weak signal, not absent knowledge**: check the candidates\' scores and titles before concluding the KB lacks the content. A truly empty response means nothing even weakly related exists.\n11. **Relations express how documents relate; lifecycle tells you if knowledge is still good.** Use `cerefox_set_relation` when one document supersedes, contradicts, references, or continues another. `supersedes` marks the target **superseded**; `contradicts` marks **both** stale; `related_to`/`duplicates`/`contradicts` are symmetric (both directions written). Any other type string is accepted without special behaviour. When a search result or `cerefox_get_relations` shows a neighbour marked `[superseded]` or `[stale]`, say so rather than presenting it as current.\n12. **Project memberships — non-destructive by default**: on `cerefox_ingest` updates, **`project_name` (singular) is a non-destructive add** (ensures membership, preserves others). Use **`project_names` (list)** when you want to set the doc\'s full project set in one call (destructive replace). For metadata-only project changes without writing content, use **`cerefox_set_document_projects(document_id, project_names)`** — that tool is the destructive-replace contract made explicit. Never call `cerefox_set_document_projects` with a single name when you mean "add" — that would REMOVE the doc from all other projects. When in doubt, use `cerefox_ingest` with singular `project_name`.',
54981
55458
  "Update Workflow (ID-based -- preferred)": `## Update Workflow (ID-based -- preferred)
54982
55459
 
54983
55460
  \`\`\`
@@ -54989,7 +55466,7 @@ ingest(title="Same Title", content="...", document_id="abc123",
54989
55466
  On a **conflict** error: get_document again (fresh content + fresh hash) -> merge your changes -> retry with the new hash.`,
54990
55467
  "Update Workflow (title-based -- fallback)": '## Update Workflow (title-based -- fallback)\n\n```\nsearch("topic") -> find doc (note its hash) -> modify ->\ningest(title="Same Title", content="...", update_if_exists=true,\n expected_content_hash="<the hash you read>", author="my-agent")\n```',
54991
55468
  "Catch-Up Workflow": '## Catch-Up Workflow\n\n```\nmetadata_search(metadata_filter={"type": "decision-log"}, updated_since="2026-03-28T00:00:00Z")\n```',
54992
- "CLI fallback (when MCP is unavailable)": '## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. The canonical invocation is plain **`cerefox <subcommand>`** (the TypeScript CLI, installed via `npm install -g @cerefox/memory`). It uses a resource-verb shape (`cerefox document get`, `cerefox project list`, …).\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); common flags also have single-letter short forms (`-f`, `-p`, `-c`, `-m`, `-u`, `-a`, `-r`). Use the canonical long name (what `--help` shows) or its short form — there are no long-form aliases like `--filter` or `--count`.\n\n| MCP tool | CLI |\n|---|---|\n| `cerefox_search` | `cerefox search "<q>" --requestor "<your-name>"` |\n| `cerefox_ingest` (paste) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --author "<your-name>" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --document-id "<uuid>" --expected-content-hash "<hash>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --requestor "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --requestor "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --requestor "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --requestor "<your-name>"` (list a project: `cerefox document list --project <name>`) |\n| `cerefox_set_document_projects` | `cerefox document set-projects <id> <name...> --author "<your-name>" --author-type agent` (or `--clear` to remove all) |\n| `cerefox_get_audit_log` | `cerefox audit list --requestor "<your-name>"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox guides show agent-quick-reference` (or `cerefox guides list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`document ingest`, `document ingest-dir`): `--author "<your-name>" --author-type agent`\n- Reads: `--requestor "<your-name>"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.'
55469
+ "CLI fallback (when MCP is unavailable)": '## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. The canonical invocation is plain **`cerefox <subcommand>`** (the TypeScript CLI, installed via `npm install -g @cerefox/memory`). It uses a resource-verb shape (`cerefox document get`, `cerefox project list`, …).\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); common flags also have single-letter short forms (`-f`, `-p`, `-c`, `-m`, `-u`, `-a`, `-r`). Use the canonical long name (what `--help` shows) or its short form — there are no long-form aliases like `--filter` or `--count`.\n\n| MCP tool | CLI |\n|---|---|\n| `cerefox_search` | `cerefox search "<q>" --requestor "<your-name>"` |\n| `cerefox_ingest` (paste) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --author "<your-name>" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf \'...\' \\| cerefox document ingest --paste --title "<t>" --document-id "<uuid>" --expected-content-hash "<hash>" --author "<your-name>" --author-type agent` |\n| `cerefox_get_document` | `cerefox document get <id> --version-id <vid> --requestor "<your-name>"` |\n| `cerefox_list_versions` | `cerefox document version list <id> --requestor "<your-name>"` |\n| `cerefox_list_projects` | `cerefox project list --requestor "<your-name>"` |\n| `cerefox_list_metadata_keys` | `cerefox metadata keys` |\n| `cerefox_set_relation` | Link two documents (`source --rel_type--> target`) | `source_id`, `target_id`, `rel_type` (required), `metadata`, `author` |\n| `cerefox_delete_relation` | Remove a relation | `source_id`, `target_id`, `rel_type` |\n| `cerefox_get_relations` | All relations touching a document, both directions | `document_id` |\n| `cerefox_get_neighbors` | Walk the graph along ONE relation type | `document_id`, `rel_type` (required), `depth`, `from_time`, `to_time`, `limit` |\n| `cerefox_metadata_search` | `cerefox metadata search --metadata-filter \'<json>\' --requestor "<your-name>"` (list a project: `cerefox document list --project <name>`) |\n| `cerefox_set_document_projects` | `cerefox document set-projects <id> <name...> --author "<your-name>" --author-type agent` (or `--clear` to remove all) |\n| `cerefox_get_audit_log` | `cerefox audit list --requestor "<your-name>"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox guides show agent-quick-reference` (or `cerefox guides list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`document ingest`, `document ingest-dir`): `--author "<your-name>" --author-type agent`\n- Reads: `--requestor "<your-name>"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.'
54993
55470
  };
54994
55471
  HELP_SECTION_HEADINGS = ["Tools", "Essential Rules", "Update Workflow (ID-based -- preferred)", "Update Workflow (title-based -- fallback)", "Catch-Up Workflow", "CLI fallback (when MCP is unavailable)"];
54995
55472
  });
@@ -55577,10 +56054,12 @@ async function handler9(supabase, args, ctx) {
55577
56054
  const project_name = args.project_name;
55578
56055
  const match_count = args.match_count ?? 5;
55579
56056
  const mode = args.mode ?? "docs";
55580
- const alpha = args.alpha ?? 0.7;
55581
- const min_score = args.min_score ?? getMinSearchScore();
56057
+ const alpha = args.alpha ?? getConfiguredSearchAlpha();
56058
+ const min_score = args.min_score ?? getConfiguredMinSearchScore();
55582
56059
  const min_term_coverage = args.min_term_coverage ?? getMinTermCoverage();
55583
56060
  const coverageParam = min_term_coverage !== undefined ? { p_min_term_coverage: min_term_coverage } : {};
56061
+ const scoreParam = min_score !== undefined ? { p_min_score: min_score } : {};
56062
+ const alphaParam = alpha !== undefined ? { p_alpha: alpha } : {};
55584
56063
  const metadata_filter = args.metadata_filter ?? null;
55585
56064
  const requested_max_bytes = args.max_bytes;
55586
56065
  const ceiling = getMaxResponseBytes();
@@ -55621,10 +56100,10 @@ async function handler9(supabase, args, ctx) {
55621
56100
  p_query_text: query,
55622
56101
  p_query_embedding: embedding,
55623
56102
  p_match_count: match_count,
55624
- p_alpha: alpha,
55625
56103
  p_use_upgrade: false,
55626
56104
  p_project_id: projectId,
55627
- p_min_score: min_score,
56105
+ ...alphaParam,
56106
+ ...scoreParam,
55628
56107
  ...metaFilterParam,
55629
56108
  ...coverageParam
55630
56109
  };
@@ -55634,9 +56113,9 @@ async function handler9(supabase, args, ctx) {
55634
56113
  p_query_text: query,
55635
56114
  p_query_embedding: embedding,
55636
56115
  p_match_count: match_count,
55637
- p_alpha: alpha,
55638
56116
  p_project_id: projectId,
55639
- p_min_score: min_score,
56117
+ ...alphaParam,
56118
+ ...scoreParam,
55640
56119
  ...metaFilterParam,
55641
56120
  ...coverageParam
55642
56121
  };
@@ -55805,6 +56284,7 @@ var init_set_document_projects = __esm(() => {
55805
56284
  var ALL_TOOLS, TOOLS_BY_NAME;
55806
56285
  var init_mcp_tools = __esm(() => {
55807
56286
  init_audit_log();
56287
+ init_relations();
55808
56288
  init_get_document();
55809
56289
  init_get_help();
55810
56290
  init_ingest();
@@ -55825,6 +56305,10 @@ var init_mcp_tools = __esm(() => {
55825
56305
  listProjectsTool,
55826
56306
  setDocumentProjectsTool,
55827
56307
  auditLogTool,
56308
+ setRelationTool,
56309
+ deleteRelationTool,
56310
+ getRelationsTool,
56311
+ getNeighborsTool,
55828
56312
  getHelpTool
55829
56313
  ];
55830
56314
  TOOLS_BY_NAME = Object.fromEntries(ALL_TOOLS.map((t) => [t.name, t]));
@@ -69039,7 +69523,7 @@ var init_server3 = __esm(() => {
69039
69523
  // src/bin/cerefox.ts
69040
69524
  init_cli_core();
69041
69525
 
69042
- // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/esm.mjs
69526
+ // ../../node_modules/.bun/commander@14.0.3/node_modules/commander/esm.mjs
69043
69527
  var import__ = __toESM(require_commander(), 1);
69044
69528
  var {
69045
69529
  program,
@@ -69464,6 +69948,18 @@ var CONFIG_KEYS = [
69464
69948
  {
69465
69949
  key: "requestor_identity_format",
69466
69950
  description: "Regex the requestor/author must match (only enforced when the above is on)."
69951
+ },
69952
+ {
69953
+ key: "min_search_score",
69954
+ description: "0–1 — minimum cosine similarity for vector-side results. Default 0.5 (use 0.6 with the local embedder)."
69955
+ },
69956
+ {
69957
+ key: "min_term_coverage",
69958
+ description: "0–1 — fraction of a query's meaningful terms a keyword OR-fallback match must cover to count as confident. Default 0.5."
69959
+ },
69960
+ {
69961
+ key: "search_alpha",
69962
+ description: "0–1 — hybrid fusion weight: 1 = pure semantic, 0 = pure keyword. Default 0.7."
69467
69963
  }
69468
69964
  ];
69469
69965
  function action4(options) {
@@ -75006,8 +75502,8 @@ import { homedir as homedir6 } from "node:os";
75006
75502
  import { join as join9 } from "node:path";
75007
75503
 
75008
75504
  // ../../_shared/ef-meta/index.ts
75009
- var EF_VERSION = "1.0.5";
75010
- var EF_LAST_CHANGED = "1.0.4";
75505
+ var EF_VERSION = "1.1.0-beta.1";
75506
+ var EF_LAST_CHANGED = "1.1.0-beta.1";
75011
75507
 
75012
75508
  // src/cli/util/checks.ts
75013
75509
  init_config();
@@ -75654,11 +76150,17 @@ async function action15(options) {
75654
76150
  process.exit(1);
75655
76151
  }
75656
76152
  if (!options.json) {
75657
- println(cErr.green("✓") + ` All checks passed${warnCount > 0 ? ` (${warnCount} warning${warnCount === 1 ? "" : "s"})` : ""}.`);
76153
+ if (warnCount > 0) {
76154
+ println(cErr.yellow("⚠ ") + `${warnCount} warning${warnCount === 1 ? "" : "s"} — see above.`);
76155
+ } else {
76156
+ println(cErr.green("✓") + " All checks passed.");
76157
+ }
75658
76158
  }
76159
+ if (options.strict && warnCount > 0)
76160
+ process.exit(1);
75659
76161
  }
75660
76162
  function registerDoctor(program2) {
75661
- program2.command("doctor").description("Run diagnostic checks against the installed Cerefox.").option("--json", "Emit machine-readable JSON (no colours, structured output).").action(action15);
76163
+ program2.command("doctor").description("Run diagnostic checks against the installed Cerefox.").option("--json", "Emit machine-readable JSON (no colours, structured output).").option("--strict", "Exit non-zero when any check warns (default: only errors fail).").action(action15);
75662
76164
  }
75663
76165
 
75664
76166
  // src/cli/commands/get-audit-log.ts
@@ -75705,6 +76207,134 @@ function registerGetAuditLog(program2) {
75705
76207
  program2.command("get-audit-log").description("Query the audit log with optional filters.").option("-d, --document-id <uuid>", "Filter by document.").option("-a, --author <name>", "Filter by author.").option("-o, --operation <type>", "Filter by operation: create, update-content, update-metadata, delete, restore.").option("--since <iso>", "Lower-bound ISO timestamp.").option("--until <iso>", "Upper-bound ISO timestamp.").option("-l, --limit <n>", "Maximum entries (max 200).", "50").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action16);
75706
76208
  }
75707
76209
 
76210
+ // src/cli/commands/relation.ts
76211
+ init_cli_core();
76212
+ init_client();
76213
+ var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
76214
+ function requireUuid(value, field) {
76215
+ const v = value.trim();
76216
+ if (!UUID_RE3.test(v)) {
76217
+ throw userError(`${field} must be a document UUID (got: ${value}).`, "Search for the document first — results show `[id: <uuid>]`.");
76218
+ }
76219
+ return v;
76220
+ }
76221
+ function registerRelationSet(program2) {
76222
+ program2.command("set <source-id> <rel-type> <target-id>").description("Link two documents: <source> --<rel-type>--> <target>.").option("--meta <json>", "JSON metadata for the edge (note, confidence, …).").option("--author <name>", "Who is creating this relation.").option("--json", "Emit machine-readable JSON.").action(async (sourceId, relType, targetId, options) => {
76223
+ const source = requireUuid(sourceId, "source-id");
76224
+ const target = requireUuid(targetId, "target-id");
76225
+ if (!relType.trim())
76226
+ throw userError("rel-type is required.");
76227
+ let metadata = {};
76228
+ if (options.meta) {
76229
+ try {
76230
+ metadata = JSON.parse(options.meta);
76231
+ } catch {
76232
+ throw userError(`--meta must be valid JSON (got: ${options.meta})`);
76233
+ }
76234
+ }
76235
+ const author = resolveAuthor(options.author);
76236
+ const authorType = resolveAuthorType(undefined);
76237
+ if (author === "unknown") {
76238
+ warn("No --author / CEREFOX_AUTHOR_NAME set — audit log will record this write as 'unknown'.");
76239
+ }
76240
+ const client = getClient();
76241
+ const data = await client.rpc("cerefox_set_relation", {
76242
+ p_source_id: source,
76243
+ p_target_id: target,
76244
+ p_rel_type: relType.trim(),
76245
+ p_author: author,
76246
+ p_author_type: authorType,
76247
+ p_metadata: metadata
76248
+ });
76249
+ const symmetric = Boolean(data?.[0]?.is_symmetric);
76250
+ if (options.json) {
76251
+ printJson({ source, target, rel_type: relType.trim(), symmetric });
76252
+ return;
76253
+ }
76254
+ println(c.green("✓ ") + `${source} --${relType.trim()}--> ${target}` + (symmetric ? c.dim(" (symmetric — reverse edge written too)") : ""));
76255
+ if (relType.trim() === "supersedes")
76256
+ println(c.dim(" Target marked superseded."));
76257
+ if (relType.trim() === "contradicts")
76258
+ println(c.dim(" Both documents marked stale."));
76259
+ });
76260
+ }
76261
+ function registerRelationDelete(program2) {
76262
+ program2.command("delete <source-id> <rel-type> <target-id>").description("Remove a relation (symmetric types remove both directions).").option("--author <name>", "Who is removing this relation.").action(async (sourceId, relType, targetId, options) => {
76263
+ const source = requireUuid(sourceId, "source-id");
76264
+ const target = requireUuid(targetId, "target-id");
76265
+ const author = resolveAuthor(options.author);
76266
+ const authorType = resolveAuthorType(undefined);
76267
+ if (author === "unknown") {
76268
+ warn("No --author / CEREFOX_AUTHOR_NAME set — audit log will record this write as 'unknown'.");
76269
+ }
76270
+ const client = getClient();
76271
+ const removed = await client.rpc("cerefox_delete_relation", {
76272
+ p_source_id: source,
76273
+ p_target_id: target,
76274
+ p_rel_type: relType.trim(),
76275
+ p_author: author,
76276
+ p_author_type: authorType
76277
+ });
76278
+ if (!removed) {
76279
+ println(c.dim("(no such relation — nothing removed)"));
76280
+ return;
76281
+ }
76282
+ println(c.green("✓ ") + `Removed ${removed} relation row(s).`);
76283
+ println(c.dim(" Lifecycle status left as-is — a document may be superseded by something else."));
76284
+ });
76285
+ }
76286
+ function registerRelationList(program2) {
76287
+ program2.command("list <document-id>").description("Show every relation touching a document (both directions).").option("--json", "Emit machine-readable JSON.").action(async (documentId, options) => {
76288
+ const id = requireUuid(documentId, "document-id");
76289
+ const client = getClient();
76290
+ const rows = await client.rpc("cerefox_get_relations", {
76291
+ p_document_id: id
76292
+ }) ?? [];
76293
+ if (options.json) {
76294
+ printJson(rows);
76295
+ return;
76296
+ }
76297
+ if (rows.length === 0) {
76298
+ println(c.dim("(no relations)"));
76299
+ return;
76300
+ }
76301
+ for (const r of rows) {
76302
+ const arrow = r.direction === "outbound" ? "→" : "←";
76303
+ const life = r.other_lifecycle && r.other_lifecycle !== "active" ? c.yellow(` [${r.other_lifecycle}]`) : "";
76304
+ println(` ${arrow} ${c.bold(r.rel_type)} ${r.other_title}${life} ${c.dim(`[id: ${r.other_id}]`)}`);
76305
+ }
76306
+ });
76307
+ }
76308
+ function registerRelationNeighbors(program2) {
76309
+ program2.command("neighbors <document-id> <rel-type>").description("Walk the graph from a document along one relation type.").option("-d, --depth <n>", "Hops to follow (1–5).", "1").option("-l, --limit <n>", "Max documents to return.", "50").option("--json", "Emit machine-readable JSON.").action(async (documentId, relType, options) => {
76310
+ const id = requireUuid(documentId, "document-id");
76311
+ const depth = Number.parseInt(options.depth ?? "1", 10);
76312
+ const limit = Number.parseInt(options.limit ?? "50", 10);
76313
+ if (Number.isNaN(depth) || depth < 1 || depth > 5) {
76314
+ throw userError("--depth must be between 1 and 5.");
76315
+ }
76316
+ const client = getClient();
76317
+ const rows = await client.rpc("cerefox_get_neighbors", {
76318
+ p_document_id: id,
76319
+ p_rel_type: relType.trim(),
76320
+ p_depth: depth,
76321
+ p_limit: Number.isNaN(limit) ? 50 : limit
76322
+ }) ?? [];
76323
+ if (options.json) {
76324
+ printJson(rows);
76325
+ return;
76326
+ }
76327
+ if (rows.length === 0) {
76328
+ println(c.dim(`(nothing reachable via "${relType.trim()}")`));
76329
+ return;
76330
+ }
76331
+ for (const r of rows) {
76332
+ const life = r.lifecycle_status && r.lifecycle_status !== "active" ? c.yellow(` [${r.lifecycle_status}]`) : "";
76333
+ println(` ${c.dim(`depth ${r.depth}`)} ${r.direction === "outbound" ? "→" : "←"} ` + `${r.title}${life} ${c.dim(`[id: ${r.document_id}]`)}`);
76334
+ }
76335
+ });
76336
+ }
76337
+
75708
76338
  // src/cli/commands/get-doc.ts
75709
76339
  init_cli_core();
75710
76340
  init_cli_core();
@@ -77480,8 +78110,10 @@ async function action29(query, options) {
77480
78110
  throw userError("Empty query.");
77481
78111
  }
77482
78112
  const matchCount = parsePositiveInt(options.matchCount, "--match-count", 5);
77483
- const alpha = parseFloat01(options.alpha, "--alpha", 0.7);
77484
- const minScore = parseFloat01(options.minScore, "--min-score", getMinSearchScore());
78113
+ const envAlpha = getConfiguredSearchAlpha();
78114
+ const alphaParam = options.alpha !== undefined ? { p_alpha: parseFloat01(options.alpha, "--alpha", envAlpha ?? 0.7) } : envAlpha !== undefined ? { p_alpha: envAlpha } : {};
78115
+ const envScore = getConfiguredMinSearchScore();
78116
+ const scoreParam = options.minScore !== undefined ? { p_min_score: parseFloat01(options.minScore, "--min-score", envScore ?? 0.5) } : envScore !== undefined ? { p_min_score: envScore } : {};
77485
78117
  const envCoverage = getMinTermCoverage();
77486
78118
  const coverageParam = options.minTermCoverage !== undefined ? { p_min_term_coverage: parseFloat01(options.minTermCoverage, "--min-term-coverage", envCoverage ?? 0.5) } : envCoverage !== undefined ? { p_min_term_coverage: envCoverage } : {};
77487
78119
  const maxBytes = parseNonNegativeInt(options.maxBytes, "--max-bytes", getMaxResponseBytes());
@@ -77523,10 +78155,10 @@ async function action29(query, options) {
77523
78155
  p_query_text: query,
77524
78156
  p_query_embedding: embedding,
77525
78157
  p_match_count: matchCount,
77526
- p_alpha: alpha,
77527
78158
  p_use_upgrade: false,
77528
78159
  p_project_id: projectId,
77529
- p_min_score: minScore,
78160
+ ...alphaParam,
78161
+ ...scoreParam,
77530
78162
  ...metaFilterParam,
77531
78163
  ...coverageParam
77532
78164
  };
@@ -77536,9 +78168,9 @@ async function action29(query, options) {
77536
78168
  p_query_text: query,
77537
78169
  p_query_embedding: embedding,
77538
78170
  p_match_count: matchCount,
77539
- p_alpha: alpha,
77540
78171
  p_project_id: projectId,
77541
- p_min_score: minScore,
78172
+ ...alphaParam,
78173
+ ...scoreParam,
77542
78174
  ...metaFilterParam,
77543
78175
  ...coverageParam
77544
78176
  };
@@ -77639,7 +78271,7 @@ async function action29(query, options) {
77639
78271
  }
77640
78272
  }
77641
78273
  function registerSearch(program2) {
77642
- program2.command("search").description("Search the knowledge base (hybrid FTS + semantic).").argument("<query>", "Natural-language search query.").option("-c, --match-count <n>", "Maximum number of documents to return.", "5").option("-p, --project-name <name>", "Filter results to a specific project.").option("-f, --metadata-filter <json>", "JSON containment filter; only docs whose metadata contains ALL pairs are returned.").option("--mode <mode>", "Search mode: docs (default), hybrid, fts.", "docs").option("--alpha <float>", "Semantic weight 0..1 (default: 0.7).", "0.7").option("--min-score <float>", "Minimum cosine similarity threshold (default: CEREFOX_MIN_SEARCH_SCORE; else 0.5, or 0.6 with the local embedder).").option("--min-term-coverage <float>", "OR-fallback keyword matches must cover at least this fraction of the query's meaningful terms to count as confident hits (default: CEREFOX_MIN_TERM_COVERAGE; else the server default 0.5; needs schema ≥ 0.9.1).").option("--max-bytes <n>", "Response size budget in bytes (default: CEREFOX_MAX_RESPONSE_BYTES or 200000).").option("-r, --requestor <name>", "Agent / user name (recorded in usage log).").option("--json", "Emit machine-readable JSON instead of the default text.").option("--only-metadata", "List matching docs (id, score, chunks, chars, partial/full) WITHOUT their content — like the web UI's collapsed result list. Grab a [id:…] then `cerefox document get <id>`.").action(action29);
78274
+ program2.command("search").description("Search the knowledge base (hybrid FTS + semantic).").argument("<query>", "Natural-language search query.").option("-c, --match-count <n>", "Maximum number of documents to return.", "5").option("-p, --project-name <name>", "Filter results to a specific project.").option("-f, --metadata-filter <json>", "JSON containment filter; only docs whose metadata contains ALL pairs are returned.").option("--mode <mode>", "Search mode: docs (default), hybrid, fts.", "docs").option("--alpha <float>", "Semantic weight 0..1 (default: CEREFOX_SEARCH_ALPHA; else 0.7).").option("--min-score <float>", "Minimum cosine similarity threshold (default: CEREFOX_MIN_SEARCH_SCORE; else 0.5, or 0.6 with the local embedder).").option("--min-term-coverage <float>", "OR-fallback keyword matches must cover at least this fraction of the query's meaningful terms to count as confident hits (default: CEREFOX_MIN_TERM_COVERAGE; else the server default 0.5; needs schema ≥ 0.9.1).").option("--max-bytes <n>", "Response size budget in bytes (default: CEREFOX_MAX_RESPONSE_BYTES or 200000).").option("-r, --requestor <name>", "Agent / user name (recorded in usage log).").option("--json", "Emit machine-readable JSON instead of the default text.").option("--only-metadata", "List matching docs (id, score, chunks, chars, partial/full) WITHOUT their content — like the web UI's collapsed result list. Grab a [id:…] then `cerefox document get <id>`.").action(action29);
77643
78275
  }
77644
78276
 
77645
78277
  // src/cli/commands/self-update.ts
@@ -77722,7 +78354,8 @@ async function action30(options) {
77722
78354
  stdio: "inherit"
77723
78355
  });
77724
78356
  if (result.status !== 0) {
77725
- throw systemError(`${runtime.description} install failed (exit ${result.status}).`, `Try the manual command: \`${runtime.command} ${runtime.args(target).join(" ")}\``);
78357
+ const notFound2 = result.error?.code === "ENOENT";
78358
+ throw systemError(notFound2 ? `${runtime.description} was not found on PATH (tried \`${runtime.command}\`).` : `${runtime.description} install failed (exit ${result.status}).`, notFound2 ? `Install ${runtime.description}, or upgrade manually: \`${runtime.command} ${runtime.args(target).join(" ")}\`` : `Try the manual command: \`${runtime.command} ${runtime.args(target).join(" ")}\``);
77726
78359
  }
77727
78360
  println("");
77728
78361
  println(c.green(`✓ Upgraded to ${target}.`));
@@ -81440,7 +82073,7 @@ function logWebUsage(ctx, params) {
81440
82073
  }
81441
82074
 
81442
82075
  // src/web/routes/discovery.ts
81443
- var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
82076
+ var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
81444
82077
  var DOC_COLS = "id, title, source, source_path, content_hash, metadata, chunk_count, total_chars, review_status, created_at, updated_at, deleted_at";
81445
82078
  function jsonByteLength(value) {
81446
82079
  return Buffer.byteLength(JSON.stringify(value), "utf8");
@@ -81643,7 +82276,7 @@ async function runSearch(ctx, opts) {
81643
82276
  p_query_text: query,
81644
82277
  p_query_embedding: embedding,
81645
82278
  p_match_count: count,
81646
- p_alpha: 0.7,
82279
+ p_alpha: getSearchAlpha(),
81647
82280
  p_use_upgrade: false,
81648
82281
  p_project_id: projectId,
81649
82282
  p_min_score: getMinSearchScore()
@@ -81659,7 +82292,7 @@ async function runSearch(ctx, opts) {
81659
82292
  p_query_text: query,
81660
82293
  p_query_embedding: embedding,
81661
82294
  p_match_count: Math.min(count, 5),
81662
- p_alpha: 0.7,
82295
+ p_alpha: getSearchAlpha(),
81663
82296
  p_project_id: projectId,
81664
82297
  p_min_score: getMinSearchScore()
81665
82298
  };
@@ -81912,7 +82545,7 @@ function registerDiscoveryRoutes(app, ctx) {
81912
82545
  if (!path) {
81913
82546
  return c2.json({ tried_path: "", anchor, matches: [] });
81914
82547
  }
81915
- if (UUID_RE3.test(path)) {
82548
+ if (UUID_RE4.test(path)) {
81916
82549
  if (path === fromDocId) {
81917
82550
  return c2.json({ tried_path: path, anchor, matches: [] });
81918
82551
  }
@@ -83421,6 +84054,11 @@ Learn more:
83421
84054
  const metadata = program2.command("metadata").description("Metadata: keys, search.");
83422
84055
  moveInto(metadata, registerListMetadataKeys, "keys");
83423
84056
  moveInto(metadata, registerMetadataSearch, "search");
84057
+ const relation = program2.command("relation").description("Document relations: set, delete, list, neighbors.");
84058
+ registerRelationSet(relation);
84059
+ registerRelationDelete(relation);
84060
+ registerRelationList(relation);
84061
+ registerRelationNeighbors(relation);
83424
84062
  const audit = program2.command("audit").description("Audit log: list.");
83425
84063
  moveInto(audit, registerGetAuditLog, "list");
83426
84064
  const config2 = program2.command("config").description("Runtime config: list, get, set.");