@cerefox/memory 1.0.8 → 1.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT_QUICK_REFERENCE.md +11 -2
- package/dist/bin/cerefox.js +956 -201
- package/dist/frontend/assets/index-C1JXZA9m.css +1 -0
- package/dist/frontend/assets/index-CMNl_LF9.js +121 -0
- package/dist/frontend/assets/index-CMNl_LF9.js.map +1 -0
- package/dist/frontend/index.html +2 -2
- package/dist/server-assets/_shared/ef-meta/index.ts +2 -2
- package/dist/server-assets/_shared/mcp-tools/_utils.ts +26 -0
- package/dist/server-assets/_shared/mcp-tools/feature-flags.ts +68 -0
- package/dist/server-assets/_shared/mcp-tools/get-help-content.ts +4 -4
- package/dist/server-assets/_shared/mcp-tools/index.ts +42 -1
- package/dist/server-assets/_shared/mcp-tools/relations.ts +284 -0
- package/dist/server-assets/_shared/mcp-tools/search.ts +14 -8
- package/dist/server-assets/db/migrations/0014_document_relations.sql +64 -0
- package/dist/server-assets/db/rpcs.sql +294 -14
- package/dist/server-assets/db/schema.sql +40 -2
- package/dist/server-assets/supabase/functions/cerefox-mcp/index.ts +25 -8
- package/docs/guides/cli.md +0 -36
- package/docs/guides/configuration.md +21 -0
- package/docs/guides/migration-1.0.md +1 -3
- package/docs/guides/ops-scripts.md +2 -2
- package/docs/guides/setup-local.md +3 -1
- package/docs/guides/setup-supabase.md +35 -4
- package/docs/guides/upgrading.md +1 -1
- package/package.json +3 -3
- package/dist/frontend/assets/index-Asx5wD7g.css +0 -1
- package/dist/frontend/assets/index-VeqA60-v.js +0 -125
- package/dist/frontend/assets/index-VeqA60-v.js.map +0 -1
package/dist/bin/cerefox.js
CHANGED
|
@@ -5340,7 +5340,7 @@ var init_cli_core = __esm(() => {
|
|
|
5340
5340
|
init_prompts();
|
|
5341
5341
|
});
|
|
5342
5342
|
|
|
5343
|
-
// ../../node_modules/.bun/commander@
|
|
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@
|
|
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.
|
|
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
|
-
|
|
5401
|
+
_collectValue(value, previous) {
|
|
5402
5402
|
if (previous === this.defaultValue || !Array.isArray(previous)) {
|
|
5403
5403
|
return [value];
|
|
5404
5404
|
}
|
|
5405
|
-
|
|
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.
|
|
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@
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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
|
-
|
|
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
|
|
5606
|
+
const extraDescription = `(${extraInfo.join(", ")})`;
|
|
5598
5607
|
if (argument.description) {
|
|
5599
|
-
return `${argument.description} ${
|
|
5608
|
+
return `${argument.description} ${extraDescription}`;
|
|
5600
5609
|
}
|
|
5601
|
-
return
|
|
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
|
|
5608
|
-
|
|
5609
|
-
|
|
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;
|
|
5637
|
+
const helpWidth = helper.helpWidth ?? 80;
|
|
5638
|
+
function callFormatItem(term, description) {
|
|
5639
|
+
return helper.formatItem(term, termWidth, description, helper);
|
|
5616
5640
|
}
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
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.
|
|
5648
|
+
helper.boxWrap(helper.styleCommandDescription(commandDescription), helpWidth),
|
|
5626
5649
|
""
|
|
5627
5650
|
]);
|
|
5628
5651
|
}
|
|
5629
5652
|
const argumentList = helper.visibleArguments(cmd).map((argument) => {
|
|
5630
|
-
return
|
|
5653
|
+
return callFormatItem(helper.styleArgumentTerm(helper.argumentTerm(argument)), helper.styleArgumentDescription(helper.argumentDescription(argument)));
|
|
5631
5654
|
});
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
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 (
|
|
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
|
|
5665
|
+
return callFormatItem(helper.styleOptionTerm(helper.optionTerm(option)), helper.styleOptionDescription(helper.optionDescription(option)));
|
|
5644
5666
|
});
|
|
5645
|
-
|
|
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
|
|
5654
|
-
|
|
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
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
const
|
|
5671
|
-
if (
|
|
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
|
|
5674
|
-
const
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
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@
|
|
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
|
-
|
|
5871
|
+
_collectValue(value, previous) {
|
|
5762
5872
|
if (previous === this.defaultValue || !Array.isArray(previous)) {
|
|
5763
5873
|
return [value];
|
|
5764
5874
|
}
|
|
5765
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
5833
|
-
|
|
5950
|
+
const shortFlagExp = /^-[^-]$/;
|
|
5951
|
+
const longFlagExp = /^--[^-]/;
|
|
5952
|
+
const flagParts = flags.split(/[ |,]+/).concat("guard");
|
|
5953
|
+
if (shortFlagExp.test(flagParts[0]))
|
|
5954
|
+
shortFlag = flagParts.shift();
|
|
5955
|
+
if (longFlagExp.test(flagParts[0]))
|
|
5956
|
+
longFlag = flagParts.shift();
|
|
5957
|
+
if (!shortFlag && shortFlagExp.test(flagParts[0]))
|
|
5834
5958
|
shortFlag = flagParts.shift();
|
|
5835
|
-
|
|
5836
|
-
if (!shortFlag && /^-[^-]$/.test(longFlag)) {
|
|
5959
|
+
if (!shortFlag && longFlagExp.test(flagParts[0])) {
|
|
5837
5960
|
shortFlag = longFlag;
|
|
5838
|
-
longFlag =
|
|
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@
|
|
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@
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
6225
|
+
argument(name, description, parseArg, defaultValue) {
|
|
6074
6226
|
const argument = this.createArgument(name, description);
|
|
6075
|
-
if (typeof
|
|
6076
|
-
argument.default(defaultValue).argParser(
|
|
6227
|
+
if (typeof parseArg === "function") {
|
|
6228
|
+
argument.default(defaultValue).argParser(parseArg);
|
|
6077
6229
|
} else {
|
|
6078
|
-
argument.default(
|
|
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
|
|
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
|
-
|
|
6106
|
-
const [, helpName, helpArgs] =
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
6727
|
-
|
|
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
|
|
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
|
|
6750
|
-
value = args
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
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
|
-
|
|
7259
|
+
_getOutputContext(contextOptions) {
|
|
7033
7260
|
contextOptions = contextOptions || {};
|
|
7034
|
-
const
|
|
7035
|
-
let
|
|
7036
|
-
|
|
7037
|
-
|
|
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
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
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
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
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
|
-
|
|
7302
|
+
outputContext.write(helpInformation);
|
|
7062
7303
|
if (this._getHelpOption()?.long) {
|
|
7063
7304
|
this.emit(this._getHelpOption().long);
|
|
7064
7305
|
}
|
|
7065
|
-
this.emit("afterHelp",
|
|
7066
|
-
this._getCommandAndAncestors().forEach((command) => command.emit("afterAllHelp",
|
|
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
|
-
|
|
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
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
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
|
|
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@
|
|
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.
|
|
7441
|
+
var PKG_VERSION = "1.1.0-beta.2";
|
|
7188
7442
|
var init_meta = () => {};
|
|
7189
7443
|
|
|
7190
7444
|
// ../../node_modules/.bun/tslib@2.8.1/node_modules/tslib/tslib.js
|
|
@@ -23108,9 +23362,12 @@ function loadEnv(opts = {}) {
|
|
|
23108
23362
|
} catch {
|
|
23109
23363
|
return { path: envPath, vars: 0 };
|
|
23110
23364
|
}
|
|
23365
|
+
const configDirNamed = (env2.CEREFOX_CONFIG_DIR ?? "").trim() !== "";
|
|
23111
23366
|
let count = 0;
|
|
23112
23367
|
for (const [k, v] of Object.entries(parseDotenv(content))) {
|
|
23113
|
-
if (
|
|
23368
|
+
if (k === "CEREFOX_CONFIG_DIR")
|
|
23369
|
+
continue;
|
|
23370
|
+
if (env2[k] === undefined || configDirNamed) {
|
|
23114
23371
|
env2[k] = v;
|
|
23115
23372
|
count++;
|
|
23116
23373
|
}
|
|
@@ -25239,6 +25496,17 @@ function readUnitInterval(name) {
|
|
|
25239
25496
|
function getSearchAlpha() {
|
|
25240
25497
|
return readUnitInterval("CEREFOX_SEARCH_ALPHA") ?? DEFAULT_SEARCH_ALPHA;
|
|
25241
25498
|
}
|
|
25499
|
+
function getConfiguredMinSearchScore() {
|
|
25500
|
+
const explicit = readUnitInterval("CEREFOX_MIN_SEARCH_SCORE");
|
|
25501
|
+
if (explicit !== undefined)
|
|
25502
|
+
return explicit;
|
|
25503
|
+
if (readEnv("CEREFOX_EMBEDDER") === "local")
|
|
25504
|
+
return DEFAULT_MIN_SEARCH_SCORE_LOCAL;
|
|
25505
|
+
return;
|
|
25506
|
+
}
|
|
25507
|
+
function getConfiguredSearchAlpha() {
|
|
25508
|
+
return readUnitInterval("CEREFOX_SEARCH_ALPHA");
|
|
25509
|
+
}
|
|
25242
25510
|
function getMinSearchScore() {
|
|
25243
25511
|
const fallback = readEnv("CEREFOX_EMBEDDER") === "local" ? DEFAULT_MIN_SEARCH_SCORE_LOCAL : DEFAULT_MIN_SEARCH_SCORE;
|
|
25244
25512
|
return readUnitInterval("CEREFOX_MIN_SEARCH_SCORE") ?? fallback;
|
|
@@ -54921,6 +55189,36 @@ var init_audit_log = __esm(() => {
|
|
|
54921
55189
|
};
|
|
54922
55190
|
});
|
|
54923
55191
|
|
|
55192
|
+
// ../../_shared/mcp-tools/feature-flags.ts
|
|
55193
|
+
async function relationsEnabled(supabase) {
|
|
55194
|
+
if (cached && Date.now() - cached.at < CACHE_TTL_MS)
|
|
55195
|
+
return cached.value;
|
|
55196
|
+
try {
|
|
55197
|
+
const { data, error: error2 } = await supabase.rpc("cerefox_get_config", {
|
|
55198
|
+
p_key: "relations_enabled"
|
|
55199
|
+
});
|
|
55200
|
+
if (error2)
|
|
55201
|
+
throw new Error(error2.message);
|
|
55202
|
+
const value = String(data ?? "").trim().toLowerCase() === "true";
|
|
55203
|
+
cached = { value, at: Date.now() };
|
|
55204
|
+
return value;
|
|
55205
|
+
} catch {
|
|
55206
|
+
return false;
|
|
55207
|
+
}
|
|
55208
|
+
}
|
|
55209
|
+
function disabledToolMessage(name) {
|
|
55210
|
+
return `${name} is part of the document-relations feature, which is off by default. ` + `Enable it with: cerefox config set relations_enabled true ` + `(deployment-wide; every access path picks it up).`;
|
|
55211
|
+
}
|
|
55212
|
+
var RELATION_TOOL_NAMES, CACHE_TTL_MS = 60000, cached = null;
|
|
55213
|
+
var init_feature_flags = __esm(() => {
|
|
55214
|
+
RELATION_TOOL_NAMES = new Set([
|
|
55215
|
+
"cerefox_set_relation",
|
|
55216
|
+
"cerefox_delete_relation",
|
|
55217
|
+
"cerefox_get_relations",
|
|
55218
|
+
"cerefox_get_neighbors"
|
|
55219
|
+
]);
|
|
55220
|
+
});
|
|
55221
|
+
|
|
54924
55222
|
// ../../_shared/mcp-tools/types.ts
|
|
54925
55223
|
var McpInvalidParams;
|
|
54926
55224
|
var init_types3 = __esm(() => {
|
|
@@ -54932,6 +55230,204 @@ var init_types3 = __esm(() => {
|
|
|
54932
55230
|
};
|
|
54933
55231
|
});
|
|
54934
55232
|
|
|
55233
|
+
// ../../_shared/mcp-tools/relations.ts
|
|
55234
|
+
function requireUuid2(value, field) {
|
|
55235
|
+
const s = typeof value === "string" ? value.trim() : "";
|
|
55236
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s)) {
|
|
55237
|
+
throw new McpInvalidParams(`${field} must be a document UUID (got: ${String(value)})`);
|
|
55238
|
+
}
|
|
55239
|
+
return s;
|
|
55240
|
+
}
|
|
55241
|
+
async function setHandler(supabase, args, ctx) {
|
|
55242
|
+
const source = requireUuid2(args.source_id, "source_id");
|
|
55243
|
+
const target = requireUuid2(args.target_id, "target_id");
|
|
55244
|
+
const relType = typeof args.rel_type === "string" ? args.rel_type.trim() : "";
|
|
55245
|
+
if (!relType)
|
|
55246
|
+
throw new McpInvalidParams("rel_type is required");
|
|
55247
|
+
const { data, error: error2 } = await supabase.rpc("cerefox_set_relation", {
|
|
55248
|
+
p_source_id: source,
|
|
55249
|
+
p_target_id: target,
|
|
55250
|
+
p_rel_type: relType,
|
|
55251
|
+
p_author: args.author ?? "mcp-agent",
|
|
55252
|
+
p_author_type: "agent",
|
|
55253
|
+
p_metadata: args.metadata ?? {}
|
|
55254
|
+
});
|
|
55255
|
+
if (error2)
|
|
55256
|
+
throw new Error(`RPC error: ${error2.message}`);
|
|
55257
|
+
const row = Array.isArray(data) ? data[0] : data;
|
|
55258
|
+
logUsage(supabase, {
|
|
55259
|
+
operation: "set_relation",
|
|
55260
|
+
accessPath: ctx.accessPath,
|
|
55261
|
+
requestor: args.requestor,
|
|
55262
|
+
document_id: source
|
|
55263
|
+
});
|
|
55264
|
+
const both = row?.is_symmetric ? " (symmetric — the reverse edge was written too)" : "";
|
|
55265
|
+
const effect = relType === "supersedes" ? `
|
|
55266
|
+
Target marked superseded.` : relType === "contradicts" ? `
|
|
55267
|
+
Both documents marked stale.` : "";
|
|
55268
|
+
return `Relation set: ${source} --${relType}--> ${target}${both}.${effect}`;
|
|
55269
|
+
}
|
|
55270
|
+
async function deleteHandler(supabase, args, ctx) {
|
|
55271
|
+
const source = requireUuid2(args.source_id, "source_id");
|
|
55272
|
+
const target = requireUuid2(args.target_id, "target_id");
|
|
55273
|
+
const relType = typeof args.rel_type === "string" ? args.rel_type.trim() : "";
|
|
55274
|
+
if (!relType)
|
|
55275
|
+
throw new McpInvalidParams("rel_type is required");
|
|
55276
|
+
const { data, error: error2 } = await supabase.rpc("cerefox_delete_relation", {
|
|
55277
|
+
p_source_id: source,
|
|
55278
|
+
p_target_id: target,
|
|
55279
|
+
p_rel_type: relType,
|
|
55280
|
+
p_author: args.author ?? "mcp-agent",
|
|
55281
|
+
p_author_type: "agent"
|
|
55282
|
+
});
|
|
55283
|
+
if (error2)
|
|
55284
|
+
throw new Error(`RPC error: ${error2.message}`);
|
|
55285
|
+
const removed = typeof data === "number" ? data : 0;
|
|
55286
|
+
logUsage(supabase, {
|
|
55287
|
+
operation: "delete_relation",
|
|
55288
|
+
accessPath: ctx.accessPath,
|
|
55289
|
+
requestor: args.requestor,
|
|
55290
|
+
document_id: source
|
|
55291
|
+
});
|
|
55292
|
+
if (removed === 0)
|
|
55293
|
+
return "No such relation — nothing removed.";
|
|
55294
|
+
return `Removed ${removed} relation row(s): ${source} --${relType}--> ${target}. ` + "Lifecycle status is left as-is (a document may be superseded by something else too).";
|
|
55295
|
+
}
|
|
55296
|
+
async function getRelationsHandler(supabase, args, ctx) {
|
|
55297
|
+
const docId = requireUuid2(args.document_id, "document_id");
|
|
55298
|
+
const { data, error: error2 } = await supabase.rpc("cerefox_get_relations", {
|
|
55299
|
+
p_document_id: docId
|
|
55300
|
+
});
|
|
55301
|
+
if (error2)
|
|
55302
|
+
throw new Error(`RPC error: ${error2.message}`);
|
|
55303
|
+
const rows = data ?? [];
|
|
55304
|
+
logUsage(supabase, {
|
|
55305
|
+
operation: "get_relations",
|
|
55306
|
+
accessPath: ctx.accessPath,
|
|
55307
|
+
requestor: args.requestor,
|
|
55308
|
+
document_id: docId,
|
|
55309
|
+
result_count: rows.length
|
|
55310
|
+
});
|
|
55311
|
+
if (rows.length === 0)
|
|
55312
|
+
return "No relations for this document.";
|
|
55313
|
+
const lines = rows.map((r) => {
|
|
55314
|
+
const arrow = r.direction === "outbound" ? "→" : "←";
|
|
55315
|
+
const life = r.other_lifecycle && r.other_lifecycle !== "active" ? ` [${r.other_lifecycle}]` : "";
|
|
55316
|
+
return `- ${arrow} ${r.rel_type}: ${r.other_title}${life} [id: ${r.other_id}]`;
|
|
55317
|
+
});
|
|
55318
|
+
return `${rows.length} relation(s):
|
|
55319
|
+
${lines.join(`
|
|
55320
|
+
`)}`;
|
|
55321
|
+
}
|
|
55322
|
+
async function getNeighborsHandler(supabase, args, ctx) {
|
|
55323
|
+
const docId = requireUuid2(args.document_id, "document_id");
|
|
55324
|
+
const relType = typeof args.rel_type === "string" ? args.rel_type.trim() : "";
|
|
55325
|
+
if (!relType)
|
|
55326
|
+
throw new McpInvalidParams("rel_type is required — pick one type to traverse");
|
|
55327
|
+
const depth = Math.max(1, Math.min(args.depth ?? 1, 5));
|
|
55328
|
+
const { data, error: error2 } = await supabase.rpc("cerefox_get_neighbors", {
|
|
55329
|
+
p_document_id: docId,
|
|
55330
|
+
p_rel_type: relType,
|
|
55331
|
+
p_depth: depth,
|
|
55332
|
+
p_from_time: args.from_time ?? null,
|
|
55333
|
+
p_to_time: args.to_time ?? null,
|
|
55334
|
+
p_limit: Math.max(1, Math.min(args.limit ?? 50, 200))
|
|
55335
|
+
});
|
|
55336
|
+
if (error2)
|
|
55337
|
+
throw new Error(`RPC error: ${error2.message}`);
|
|
55338
|
+
const rows = data ?? [];
|
|
55339
|
+
logUsage(supabase, {
|
|
55340
|
+
operation: "get_neighbors",
|
|
55341
|
+
accessPath: ctx.accessPath,
|
|
55342
|
+
requestor: args.requestor,
|
|
55343
|
+
document_id: docId,
|
|
55344
|
+
result_count: rows.length
|
|
55345
|
+
});
|
|
55346
|
+
if (rows.length === 0)
|
|
55347
|
+
return `No documents reachable via "${relType}".`;
|
|
55348
|
+
const lines = rows.map((r) => {
|
|
55349
|
+
const life = r.lifecycle_status && r.lifecycle_status !== "active" ? ` [${r.lifecycle_status}]` : "";
|
|
55350
|
+
return `- depth ${r.depth} (${r.direction}): ${r.title}${life} [id: ${r.document_id}]`;
|
|
55351
|
+
});
|
|
55352
|
+
return `${rows.length} document(s) via "${relType}":
|
|
55353
|
+
${lines.join(`
|
|
55354
|
+
`)}`;
|
|
55355
|
+
}
|
|
55356
|
+
var KNOWN_TYPES = "related_to, references, supersedes, contradicts, duplicates, part_of, follows, reply_to", setRelationTool, deleteRelationTool, getRelationsTool, getNeighborsTool;
|
|
55357
|
+
var init_relations = __esm(() => {
|
|
55358
|
+
init_types3();
|
|
55359
|
+
setRelationTool = {
|
|
55360
|
+
name: "cerefox_set_relation",
|
|
55361
|
+
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.",
|
|
55362
|
+
inputSchema: {
|
|
55363
|
+
type: "object",
|
|
55364
|
+
required: ["source_id", "target_id", "rel_type"],
|
|
55365
|
+
properties: {
|
|
55366
|
+
source_id: { type: "string", description: "UUID of the source document" },
|
|
55367
|
+
target_id: { type: "string", description: "UUID of the target document" },
|
|
55368
|
+
rel_type: {
|
|
55369
|
+
type: "string",
|
|
55370
|
+
description: `Relation type, e.g. one of: ${KNOWN_TYPES}. Free text is allowed.`
|
|
55371
|
+
},
|
|
55372
|
+
metadata: {
|
|
55373
|
+
type: "object",
|
|
55374
|
+
description: "Optional JSON context for the edge (note, confidence, …)"
|
|
55375
|
+
},
|
|
55376
|
+
author: { type: "string", description: "Who is creating this relation" },
|
|
55377
|
+
requestor: { type: "string", description: "Name of the agent making this request" }
|
|
55378
|
+
}
|
|
55379
|
+
},
|
|
55380
|
+
handler: setHandler
|
|
55381
|
+
};
|
|
55382
|
+
deleteRelationTool = {
|
|
55383
|
+
name: "cerefox_delete_relation",
|
|
55384
|
+
description: "Remove a typed relation between two documents. Symmetric types remove both " + "directions. Lifecycle status set by an earlier relation is NOT reverted.",
|
|
55385
|
+
inputSchema: {
|
|
55386
|
+
type: "object",
|
|
55387
|
+
required: ["source_id", "target_id", "rel_type"],
|
|
55388
|
+
properties: {
|
|
55389
|
+
source_id: { type: "string", description: "UUID of the source document" },
|
|
55390
|
+
target_id: { type: "string", description: "UUID of the target document" },
|
|
55391
|
+
rel_type: { type: "string", description: "Relation type to remove" },
|
|
55392
|
+
author: { type: "string", description: "Who is removing this relation" },
|
|
55393
|
+
requestor: { type: "string", description: "Name of the agent making this request" }
|
|
55394
|
+
}
|
|
55395
|
+
},
|
|
55396
|
+
handler: deleteHandler
|
|
55397
|
+
};
|
|
55398
|
+
getRelationsTool = {
|
|
55399
|
+
name: "cerefox_get_relations",
|
|
55400
|
+
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.",
|
|
55401
|
+
inputSchema: {
|
|
55402
|
+
type: "object",
|
|
55403
|
+
required: ["document_id"],
|
|
55404
|
+
properties: {
|
|
55405
|
+
document_id: { type: "string", description: "UUID of the document" },
|
|
55406
|
+
requestor: { type: "string", description: "Name of the agent making this request" }
|
|
55407
|
+
}
|
|
55408
|
+
},
|
|
55409
|
+
handler: getRelationsHandler
|
|
55410
|
+
};
|
|
55411
|
+
getNeighborsTool = {
|
|
55412
|
+
name: "cerefox_get_neighbors",
|
|
55413
|
+
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.",
|
|
55414
|
+
inputSchema: {
|
|
55415
|
+
type: "object",
|
|
55416
|
+
required: ["document_id", "rel_type"],
|
|
55417
|
+
properties: {
|
|
55418
|
+
document_id: { type: "string", description: "UUID of the starting document" },
|
|
55419
|
+
rel_type: { type: "string", description: "The single relation type to traverse" },
|
|
55420
|
+
depth: { type: "integer", description: "How many hops to follow (1–5, default 1)" },
|
|
55421
|
+
from_time: { type: "string", description: "ISO-8601: only neighbours created on/after" },
|
|
55422
|
+
to_time: { type: "string", description: "ISO-8601: only neighbours created on/before" },
|
|
55423
|
+
limit: { type: "integer", description: "Max documents to return (default 50, max 200)" },
|
|
55424
|
+
requestor: { type: "string", description: "Name of the agent making this request" }
|
|
55425
|
+
}
|
|
55426
|
+
},
|
|
55427
|
+
handler: getNeighborsHandler
|
|
55428
|
+
};
|
|
55429
|
+
});
|
|
55430
|
+
|
|
54935
55431
|
// ../../_shared/mcp-tools/get-document.ts
|
|
54936
55432
|
async function handler2(supabase, args, ctx) {
|
|
54937
55433
|
const document_id = args.document_id;
|
|
@@ -54987,11 +55483,11 @@ var init_get_document = __esm(() => {
|
|
|
54987
55483
|
});
|
|
54988
55484
|
|
|
54989
55485
|
// ../../_shared/mcp-tools/get-help-content.ts
|
|
54990
|
-
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;
|
|
55486
|
+
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;
|
|
54991
55487
|
var init_get_help_content = __esm(() => {
|
|
54992
55488
|
HELP_SECTIONS = {
|
|
54993
|
-
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) |",
|
|
54994
|
-
"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`.',
|
|
55489
|
+
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) |",
|
|
55490
|
+
"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`.',
|
|
54995
55491
|
"Update Workflow (ID-based -- preferred)": `## Update Workflow (ID-based -- preferred)
|
|
54996
55492
|
|
|
54997
55493
|
\`\`\`
|
|
@@ -55003,7 +55499,7 @@ ingest(title="Same Title", content="...", document_id="abc123",
|
|
|
55003
55499
|
On a **conflict** error: get_document again (fresh content + fresh hash) -> merge your changes -> retry with the new hash.`,
|
|
55004
55500
|
"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```',
|
|
55005
55501
|
"Catch-Up Workflow": '## Catch-Up Workflow\n\n```\nmetadata_search(metadata_filter={"type": "decision-log"}, updated_since="2026-03-28T00:00:00Z")\n```',
|
|
55006
|
-
"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.'
|
|
55502
|
+
"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.'
|
|
55007
55503
|
};
|
|
55008
55504
|
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)"];
|
|
55009
55505
|
});
|
|
@@ -55591,10 +56087,12 @@ async function handler9(supabase, args, ctx) {
|
|
|
55591
56087
|
const project_name = args.project_name;
|
|
55592
56088
|
const match_count = args.match_count ?? 5;
|
|
55593
56089
|
const mode = args.mode ?? "docs";
|
|
55594
|
-
const alpha = args.alpha ??
|
|
55595
|
-
const min_score = args.min_score ??
|
|
56090
|
+
const alpha = args.alpha ?? getConfiguredSearchAlpha();
|
|
56091
|
+
const min_score = args.min_score ?? getConfiguredMinSearchScore();
|
|
55596
56092
|
const min_term_coverage = args.min_term_coverage ?? getMinTermCoverage();
|
|
55597
56093
|
const coverageParam = min_term_coverage !== undefined ? { p_min_term_coverage: min_term_coverage } : {};
|
|
56094
|
+
const scoreParam = min_score !== undefined ? { p_min_score: min_score } : {};
|
|
56095
|
+
const alphaParam = alpha !== undefined ? { p_alpha: alpha } : {};
|
|
55598
56096
|
const metadata_filter = args.metadata_filter ?? null;
|
|
55599
56097
|
const requested_max_bytes = args.max_bytes;
|
|
55600
56098
|
const ceiling = getMaxResponseBytes();
|
|
@@ -55635,10 +56133,10 @@ async function handler9(supabase, args, ctx) {
|
|
|
55635
56133
|
p_query_text: query,
|
|
55636
56134
|
p_query_embedding: embedding,
|
|
55637
56135
|
p_match_count: match_count,
|
|
55638
|
-
p_alpha: alpha,
|
|
55639
56136
|
p_use_upgrade: false,
|
|
55640
56137
|
p_project_id: projectId,
|
|
55641
|
-
|
|
56138
|
+
...alphaParam,
|
|
56139
|
+
...scoreParam,
|
|
55642
56140
|
...metaFilterParam,
|
|
55643
56141
|
...coverageParam
|
|
55644
56142
|
};
|
|
@@ -55648,9 +56146,9 @@ async function handler9(supabase, args, ctx) {
|
|
|
55648
56146
|
p_query_text: query,
|
|
55649
56147
|
p_query_embedding: embedding,
|
|
55650
56148
|
p_match_count: match_count,
|
|
55651
|
-
p_alpha: alpha,
|
|
55652
56149
|
p_project_id: projectId,
|
|
55653
|
-
|
|
56150
|
+
...alphaParam,
|
|
56151
|
+
...scoreParam,
|
|
55654
56152
|
...metaFilterParam,
|
|
55655
56153
|
...coverageParam
|
|
55656
56154
|
};
|
|
@@ -55816,9 +56314,22 @@ var init_set_document_projects = __esm(() => {
|
|
|
55816
56314
|
});
|
|
55817
56315
|
|
|
55818
56316
|
// ../../_shared/mcp-tools/index.ts
|
|
56317
|
+
async function listEnabledTools(supabase) {
|
|
56318
|
+
const relations = await relationsEnabled(supabase);
|
|
56319
|
+
return ALL_TOOLS.filter((t) => relations || !RELATION_TOOL_NAMES.has(t.name));
|
|
56320
|
+
}
|
|
56321
|
+
async function assertToolEnabled(supabase, name) {
|
|
56322
|
+
if (!RELATION_TOOL_NAMES.has(name))
|
|
56323
|
+
return;
|
|
56324
|
+
if (await relationsEnabled(supabase))
|
|
56325
|
+
return;
|
|
56326
|
+
throw new McpInvalidParams(disabledToolMessage(name));
|
|
56327
|
+
}
|
|
55819
56328
|
var ALL_TOOLS, TOOLS_BY_NAME;
|
|
55820
56329
|
var init_mcp_tools = __esm(() => {
|
|
55821
56330
|
init_audit_log();
|
|
56331
|
+
init_feature_flags();
|
|
56332
|
+
init_relations();
|
|
55822
56333
|
init_get_document();
|
|
55823
56334
|
init_get_help();
|
|
55824
56335
|
init_ingest();
|
|
@@ -55829,6 +56340,7 @@ var init_mcp_tools = __esm(() => {
|
|
|
55829
56340
|
init_search();
|
|
55830
56341
|
init_set_document_projects();
|
|
55831
56342
|
init_types3();
|
|
56343
|
+
init_types3();
|
|
55832
56344
|
ALL_TOOLS = [
|
|
55833
56345
|
searchTool,
|
|
55834
56346
|
ingestTool,
|
|
@@ -55839,6 +56351,10 @@ var init_mcp_tools = __esm(() => {
|
|
|
55839
56351
|
listProjectsTool,
|
|
55840
56352
|
setDocumentProjectsTool,
|
|
55841
56353
|
auditLogTool,
|
|
56354
|
+
setRelationTool,
|
|
56355
|
+
deleteRelationTool,
|
|
56356
|
+
getRelationsTool,
|
|
56357
|
+
getNeighborsTool,
|
|
55842
56358
|
getHelpTool
|
|
55843
56359
|
];
|
|
55844
56360
|
TOOLS_BY_NAME = Object.fromEntries(ALL_TOOLS.map((t) => [t.name, t]));
|
|
@@ -56030,7 +56546,7 @@ __export(exports_util, {
|
|
|
56030
56546
|
cleanRegex: () => cleanRegex,
|
|
56031
56547
|
cleanEnum: () => cleanEnum,
|
|
56032
56548
|
captureStackTrace: () => captureStackTrace,
|
|
56033
|
-
cached: () =>
|
|
56549
|
+
cached: () => cached2,
|
|
56034
56550
|
assignProp: () => assignProp,
|
|
56035
56551
|
assertNotEqual: () => assertNotEqual,
|
|
56036
56552
|
assertNever: () => assertNever,
|
|
@@ -56067,7 +56583,7 @@ function jsonStringifyReplacer(_, value) {
|
|
|
56067
56583
|
return value.toString();
|
|
56068
56584
|
return value;
|
|
56069
56585
|
}
|
|
56070
|
-
function
|
|
56586
|
+
function cached2(getter) {
|
|
56071
56587
|
const set = false;
|
|
56072
56588
|
return {
|
|
56073
56589
|
get value() {
|
|
@@ -56479,7 +56995,7 @@ var captureStackTrace, allowsEval, getParsedType2 = (data) => {
|
|
|
56479
56995
|
}, propertyKeyTypes, primitiveTypes, NUMBER_FORMAT_RANGES, BIGINT_FORMAT_RANGES;
|
|
56480
56996
|
var init_util2 = __esm(() => {
|
|
56481
56997
|
captureStackTrace = Error.captureStackTrace ? Error.captureStackTrace : (..._args) => {};
|
|
56482
|
-
allowsEval =
|
|
56998
|
+
allowsEval = cached2(() => {
|
|
56483
56999
|
if (typeof navigator !== "undefined" && navigator?.userAgent?.includes("Cloudflare")) {
|
|
56484
57000
|
return false;
|
|
56485
57001
|
}
|
|
@@ -57784,7 +58300,7 @@ var init_schemas = __esm(() => {
|
|
|
57784
58300
|
});
|
|
57785
58301
|
$ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
57786
58302
|
$ZodType.init(inst, def);
|
|
57787
|
-
const _normalized =
|
|
58303
|
+
const _normalized = cached2(() => {
|
|
57788
58304
|
const keys = Object.keys(def.shape);
|
|
57789
58305
|
for (const k of keys) {
|
|
57790
58306
|
if (!(def.shape[k] instanceof $ZodType)) {
|
|
@@ -58004,7 +58520,7 @@ var init_schemas = __esm(() => {
|
|
|
58004
58520
|
}
|
|
58005
58521
|
return propValues;
|
|
58006
58522
|
});
|
|
58007
|
-
const disc =
|
|
58523
|
+
const disc = cached2(() => {
|
|
58008
58524
|
const opts = def.options;
|
|
58009
58525
|
const map = new Map;
|
|
58010
58526
|
for (const o of opts) {
|
|
@@ -68985,7 +69501,7 @@ function buildServer() {
|
|
|
68985
69501
|
};
|
|
68986
69502
|
const server = new Server({ name: SERVER_NAME, version: PKG_VERSION }, { capabilities: { tools: {} } });
|
|
68987
69503
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
68988
|
-
tools:
|
|
69504
|
+
tools: (await listEnabledTools(supabase)).map((t) => ({
|
|
68989
69505
|
name: t.name,
|
|
68990
69506
|
description: t.description,
|
|
68991
69507
|
inputSchema: t.inputSchema
|
|
@@ -68998,6 +69514,7 @@ function buildServer() {
|
|
|
68998
69514
|
throw new McpInvalidParams(`Unknown tool: ${name}`);
|
|
68999
69515
|
}
|
|
69000
69516
|
try {
|
|
69517
|
+
await assertToolEnabled(supabase, name);
|
|
69001
69518
|
const text = await tool.handler(supabase, args, ctx);
|
|
69002
69519
|
return { content: [{ type: "text", text }] };
|
|
69003
69520
|
} catch (err) {
|
|
@@ -69053,7 +69570,7 @@ var init_server3 = __esm(() => {
|
|
|
69053
69570
|
// src/bin/cerefox.ts
|
|
69054
69571
|
init_cli_core();
|
|
69055
69572
|
|
|
69056
|
-
// ../../node_modules/.bun/commander@
|
|
69573
|
+
// ../../node_modules/.bun/commander@14.0.3/node_modules/commander/esm.mjs
|
|
69057
69574
|
var import__ = __toESM(require_commander(), 1);
|
|
69058
69575
|
var {
|
|
69059
69576
|
program,
|
|
@@ -69118,7 +69635,13 @@ function utcStamp() {
|
|
|
69118
69635
|
return d.getUTCFullYear().toString() + pad(d.getUTCMonth() + 1) + pad(d.getUTCDate()) + "T" + pad(d.getUTCHours()) + pad(d.getUTCMinutes()) + pad(d.getUTCSeconds()) + "Z";
|
|
69119
69636
|
}
|
|
69120
69637
|
async function action(options) {
|
|
69121
|
-
const
|
|
69638
|
+
const configuredDir = options.outputDir ?? process.env.CEREFOX_BACKUP_DIR;
|
|
69639
|
+
const outDir = resolve(expandHome(configuredDir ?? "~/.cerefox/backups"));
|
|
69640
|
+
if (configuredDir !== undefined && !configuredDir.startsWith("/") && !configuredDir.startsWith("~")) {
|
|
69641
|
+
println(c.yellow("⚠ ") + `Backup directory "${configuredDir}" is relative — it resolves against the ` + "current working directory, so backups land in different places depending " + "on where you run this from.");
|
|
69642
|
+
println(c.dim(` Writing to: ${outDir}`));
|
|
69643
|
+
println(c.dim(" Set an absolute path (e.g. ~/.cerefox/backups) to keep them together."));
|
|
69644
|
+
}
|
|
69122
69645
|
if (!existsSync2(outDir))
|
|
69123
69646
|
mkdirSync(outDir, { recursive: true });
|
|
69124
69647
|
const stamp = utcStamp();
|
|
@@ -69129,12 +69652,30 @@ async function action(options) {
|
|
|
69129
69652
|
try {
|
|
69130
69653
|
schemaVersion = await client.rpc("cerefox_schema_version", {}) ?? "unknown";
|
|
69131
69654
|
} catch {}
|
|
69655
|
+
const includeTrash = options.trash !== false;
|
|
69656
|
+
const BASE_COLUMNS = "id, title, content_hash, source, metadata, total_chars, chunk_count, " + "review_status, created_at, updated_at, deleted_at";
|
|
69657
|
+
const fetchDocs = (columns) => fetchAllPages((from, to) => {
|
|
69658
|
+
const q = client.raw.from("cerefox_documents").select(columns);
|
|
69659
|
+
return (includeTrash ? q : q.is("deleted_at", null)).order("created_at", { ascending: true }).order("id", { ascending: true }).range(from, to);
|
|
69660
|
+
});
|
|
69132
69661
|
let docs;
|
|
69662
|
+
let lifecycleCaptured = true;
|
|
69133
69663
|
try {
|
|
69134
|
-
docs = await
|
|
69664
|
+
docs = await fetchDocs(`${BASE_COLUMNS}, lifecycle_status`);
|
|
69135
69665
|
} catch (err) {
|
|
69136
|
-
|
|
69666
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
69667
|
+
if (!/lifecycle_status/.test(message)) {
|
|
69668
|
+
throw systemError(`Document fetch failed: ${message}`);
|
|
69669
|
+
}
|
|
69670
|
+
lifecycleCaptured = false;
|
|
69671
|
+
try {
|
|
69672
|
+
docs = await fetchDocs(BASE_COLUMNS);
|
|
69673
|
+
} catch (retryErr) {
|
|
69674
|
+
throw systemError(`Document fetch failed: ${retryErr instanceof Error ? retryErr.message : String(retryErr)}`);
|
|
69675
|
+
}
|
|
69676
|
+
println(c.dim(" (server predates lifecycle_status — captured without it)"));
|
|
69137
69677
|
}
|
|
69678
|
+
const trashedCount = docs.filter((d) => d.deleted_at != null).length;
|
|
69138
69679
|
let projects = [];
|
|
69139
69680
|
let memberships = [];
|
|
69140
69681
|
try {
|
|
@@ -69143,6 +69684,19 @@ async function action(options) {
|
|
|
69143
69684
|
} catch (err) {
|
|
69144
69685
|
throw systemError(`Project/membership fetch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
69145
69686
|
}
|
|
69687
|
+
let relations = [];
|
|
69688
|
+
try {
|
|
69689
|
+
relations = await fetchAllPages((from, to) => client.raw.from("cerefox_document_relations").select("source_id, target_id, rel_type, metadata, author, author_type, created_at").order("source_id", { ascending: true }).order("target_id", { ascending: true }).order("rel_type", { ascending: true }).range(from, to));
|
|
69690
|
+
} catch {}
|
|
69691
|
+
{
|
|
69692
|
+
const captured = new Set(docs.map((d) => d.id));
|
|
69693
|
+
const before = memberships.length;
|
|
69694
|
+
memberships = memberships.filter((m) => captured.has(m.document_id));
|
|
69695
|
+
const dropped = before - memberships.length;
|
|
69696
|
+
if (dropped > 0) {
|
|
69697
|
+
println(c.dim(` (skipped ${dropped} membership(s) belonging to trashed documents)`));
|
|
69698
|
+
}
|
|
69699
|
+
}
|
|
69146
69700
|
let chunkTotal = 0;
|
|
69147
69701
|
const enriched = [];
|
|
69148
69702
|
for (let i = 0;i < docs.length; i++) {
|
|
@@ -69166,27 +69720,37 @@ async function action(options) {
|
|
|
69166
69720
|
const payload = {
|
|
69167
69721
|
created_at: new Date().toISOString(),
|
|
69168
69722
|
cerefox_version: PKG_VERSION,
|
|
69169
|
-
backup_format:
|
|
69723
|
+
backup_format: 4,
|
|
69170
69724
|
schema_version: schemaVersion,
|
|
69171
69725
|
document_count: docs.length,
|
|
69726
|
+
trashed_count: trashedCount,
|
|
69727
|
+
includes_trash: includeTrash,
|
|
69728
|
+
includes_lifecycle_status: lifecycleCaptured,
|
|
69172
69729
|
chunk_count: chunkTotal,
|
|
69173
69730
|
project_count: projects.length,
|
|
69174
69731
|
membership_count: memberships.length,
|
|
69732
|
+
relation_count: relations.length,
|
|
69175
69733
|
projects,
|
|
69176
69734
|
memberships,
|
|
69735
|
+
relations,
|
|
69177
69736
|
documents: enriched
|
|
69178
69737
|
};
|
|
69179
69738
|
writeFileSync(dest, JSON.stringify(payload, null, 2), "utf8");
|
|
69180
69739
|
println("");
|
|
69181
69740
|
println(c.green("✓ ") + `Backup written: ${dest}`);
|
|
69182
|
-
println(c.dim(` documents: ${docs.length} · chunks: ${chunkTotal} · ` + `projects: ${projects.length} · memberships: ${memberships.length}`));
|
|
69741
|
+
println(c.dim(` documents: ${docs.length} · chunks: ${chunkTotal} · ` + `projects: ${projects.length} · memberships: ${memberships.length}` + (relations.length > 0 ? ` · relations: ${relations.length}` : "")));
|
|
69742
|
+
if (!includeTrash) {
|
|
69743
|
+
println(c.dim(" trashed documents: excluded (--no-trash)"));
|
|
69744
|
+
} else if (trashedCount > 0) {
|
|
69745
|
+
println(c.dim(` of which trashed: ${trashedCount} (restored as trash, not resurrected)`));
|
|
69746
|
+
}
|
|
69183
69747
|
if (options.git) {
|
|
69184
69748
|
println(c.yellow("⚠ ") + "--git commit is not implemented; the snapshot was written without a git checkpoint.");
|
|
69185
69749
|
println(c.dim(" Commit the backup directory yourself if you want it version-controlled."));
|
|
69186
69750
|
}
|
|
69187
69751
|
}
|
|
69188
69752
|
function registerBackup(program2) {
|
|
69189
|
-
program2.command("backup").description("Write a JSON snapshot of the knowledge base.").option("-o, --output-dir <dir>", "Snapshot output directory (default: CEREFOX_BACKUP_DIR or ~/.cerefox/backups).").option("-l, --label <label>", "Optional suffix added to the filename.").option("--include-versions", "Include archived versions in the snapshot. (v0.5: ignored — current chunks only.)").option("--git", "Commit the snapshot to the output dir as a git checkpoint. (v0.5: ignored.)").action(action);
|
|
69753
|
+
program2.command("backup").description("Write a JSON snapshot of the knowledge base.").option("-o, --output-dir <dir>", "Snapshot output directory (default: CEREFOX_BACKUP_DIR or ~/.cerefox/backups).").option("-l, --label <label>", "Optional suffix added to the filename.").option("--include-versions", "Include archived versions in the snapshot. (v0.5: ignored — current chunks only.)").option("--git", "Commit the snapshot to the output dir as a git checkpoint. (v0.5: ignored.)").option("--no-trash", "Exclude soft-deleted documents. Default: they are captured and restored as trash.").action(action);
|
|
69190
69754
|
}
|
|
69191
69755
|
|
|
69192
69756
|
// src/cli/commands/completion.ts
|
|
@@ -69497,6 +70061,22 @@ var CONFIG_KEYS = [
|
|
|
69497
70061
|
{
|
|
69498
70062
|
key: "requestor_identity_format",
|
|
69499
70063
|
description: "Regex the requestor/author must match (only enforced when the above is on)."
|
|
70064
|
+
},
|
|
70065
|
+
{
|
|
70066
|
+
key: "min_search_score",
|
|
70067
|
+
description: "0–1 — minimum cosine similarity for vector-side results. Default 0.5 (use 0.6 with the local embedder)."
|
|
70068
|
+
},
|
|
70069
|
+
{
|
|
70070
|
+
key: "min_term_coverage",
|
|
70071
|
+
description: "0–1 — fraction of a query's meaningful terms a keyword OR-fallback match must cover to count as confident. Default 0.5."
|
|
70072
|
+
},
|
|
70073
|
+
{
|
|
70074
|
+
key: "relations_enabled",
|
|
70075
|
+
description: "'true'/'false' — expose the document-relation tools to agents. Off by default; the feature is dormant until enabled (iteration 29)."
|
|
70076
|
+
},
|
|
70077
|
+
{
|
|
70078
|
+
key: "search_alpha",
|
|
70079
|
+
description: "0–1 — hybrid fusion weight: 1 = pure semantic, 0 = pure keyword. Default 0.7."
|
|
69500
70080
|
}
|
|
69501
70081
|
];
|
|
69502
70082
|
function action4(options) {
|
|
@@ -75039,8 +75619,8 @@ import { homedir as homedir6 } from "node:os";
|
|
|
75039
75619
|
import { join as join9 } from "node:path";
|
|
75040
75620
|
|
|
75041
75621
|
// ../../_shared/ef-meta/index.ts
|
|
75042
|
-
var EF_VERSION = "1.0.
|
|
75043
|
-
var EF_LAST_CHANGED = "1.0.
|
|
75622
|
+
var EF_VERSION = "1.1.0-beta.2";
|
|
75623
|
+
var EF_LAST_CHANGED = "1.1.0-beta.2";
|
|
75044
75624
|
|
|
75045
75625
|
// src/cli/util/checks.ts
|
|
75046
75626
|
init_config();
|
|
@@ -75389,7 +75969,7 @@ async function checkContentFormat() {
|
|
|
75389
75969
|
name: CONTENT_FORMAT_CHECK_NAME,
|
|
75390
75970
|
status: "skipped",
|
|
75391
75971
|
detail: `${legacy} of ${total} document(s) use the legacy reconstruction format (format 1).`,
|
|
75392
|
-
hint: "
|
|
75972
|
+
hint: "Harmless — they auto-convert on next edit. To convert them all now: `cerefox server migrate-format` (re-embeds, so try `--dry-run` first). To read what chunk formats are: `cerefox guides show content-format`."
|
|
75393
75973
|
};
|
|
75394
75974
|
} catch (err) {
|
|
75395
75975
|
return {
|
|
@@ -75687,11 +76267,17 @@ async function action15(options) {
|
|
|
75687
76267
|
process.exit(1);
|
|
75688
76268
|
}
|
|
75689
76269
|
if (!options.json) {
|
|
75690
|
-
|
|
76270
|
+
if (warnCount > 0) {
|
|
76271
|
+
println(cErr.yellow("⚠ ") + `${warnCount} warning${warnCount === 1 ? "" : "s"} — see above.`);
|
|
76272
|
+
} else {
|
|
76273
|
+
println(cErr.green("✓") + " All checks passed.");
|
|
76274
|
+
}
|
|
75691
76275
|
}
|
|
76276
|
+
if (options.strict && warnCount > 0)
|
|
76277
|
+
process.exit(1);
|
|
75692
76278
|
}
|
|
75693
76279
|
function registerDoctor(program2) {
|
|
75694
|
-
program2.command("doctor").description("Run diagnostic checks against the installed Cerefox.").option("--json", "Emit machine-readable JSON (no colours, structured output).").action(action15);
|
|
76280
|
+
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);
|
|
75695
76281
|
}
|
|
75696
76282
|
|
|
75697
76283
|
// src/cli/commands/get-audit-log.ts
|
|
@@ -75738,6 +76324,134 @@ function registerGetAuditLog(program2) {
|
|
|
75738
76324
|
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);
|
|
75739
76325
|
}
|
|
75740
76326
|
|
|
76327
|
+
// src/cli/commands/relation.ts
|
|
76328
|
+
init_cli_core();
|
|
76329
|
+
init_client();
|
|
76330
|
+
var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
76331
|
+
function requireUuid(value, field) {
|
|
76332
|
+
const v = value.trim();
|
|
76333
|
+
if (!UUID_RE3.test(v)) {
|
|
76334
|
+
throw userError(`${field} must be a document UUID (got: ${value}).`, "Search for the document first — results show `[id: <uuid>]`.");
|
|
76335
|
+
}
|
|
76336
|
+
return v;
|
|
76337
|
+
}
|
|
76338
|
+
function registerRelationSet(program2) {
|
|
76339
|
+
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) => {
|
|
76340
|
+
const source = requireUuid(sourceId, "source-id");
|
|
76341
|
+
const target = requireUuid(targetId, "target-id");
|
|
76342
|
+
if (!relType.trim())
|
|
76343
|
+
throw userError("rel-type is required.");
|
|
76344
|
+
let metadata = {};
|
|
76345
|
+
if (options.meta) {
|
|
76346
|
+
try {
|
|
76347
|
+
metadata = JSON.parse(options.meta);
|
|
76348
|
+
} catch {
|
|
76349
|
+
throw userError(`--meta must be valid JSON (got: ${options.meta})`);
|
|
76350
|
+
}
|
|
76351
|
+
}
|
|
76352
|
+
const author = resolveAuthor(options.author);
|
|
76353
|
+
const authorType = resolveAuthorType(undefined);
|
|
76354
|
+
if (author === "unknown") {
|
|
76355
|
+
warn("No --author / CEREFOX_AUTHOR_NAME set — audit log will record this write as 'unknown'.");
|
|
76356
|
+
}
|
|
76357
|
+
const client = getClient();
|
|
76358
|
+
const data = await client.rpc("cerefox_set_relation", {
|
|
76359
|
+
p_source_id: source,
|
|
76360
|
+
p_target_id: target,
|
|
76361
|
+
p_rel_type: relType.trim(),
|
|
76362
|
+
p_author: author,
|
|
76363
|
+
p_author_type: authorType,
|
|
76364
|
+
p_metadata: metadata
|
|
76365
|
+
});
|
|
76366
|
+
const symmetric = Boolean(data?.[0]?.is_symmetric);
|
|
76367
|
+
if (options.json) {
|
|
76368
|
+
printJson({ source, target, rel_type: relType.trim(), symmetric });
|
|
76369
|
+
return;
|
|
76370
|
+
}
|
|
76371
|
+
println(c.green("✓ ") + `${source} --${relType.trim()}--> ${target}` + (symmetric ? c.dim(" (symmetric — reverse edge written too)") : ""));
|
|
76372
|
+
if (relType.trim() === "supersedes")
|
|
76373
|
+
println(c.dim(" Target marked superseded."));
|
|
76374
|
+
if (relType.trim() === "contradicts")
|
|
76375
|
+
println(c.dim(" Both documents marked stale."));
|
|
76376
|
+
});
|
|
76377
|
+
}
|
|
76378
|
+
function registerRelationDelete(program2) {
|
|
76379
|
+
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) => {
|
|
76380
|
+
const source = requireUuid(sourceId, "source-id");
|
|
76381
|
+
const target = requireUuid(targetId, "target-id");
|
|
76382
|
+
const author = resolveAuthor(options.author);
|
|
76383
|
+
const authorType = resolveAuthorType(undefined);
|
|
76384
|
+
if (author === "unknown") {
|
|
76385
|
+
warn("No --author / CEREFOX_AUTHOR_NAME set — audit log will record this write as 'unknown'.");
|
|
76386
|
+
}
|
|
76387
|
+
const client = getClient();
|
|
76388
|
+
const removed = await client.rpc("cerefox_delete_relation", {
|
|
76389
|
+
p_source_id: source,
|
|
76390
|
+
p_target_id: target,
|
|
76391
|
+
p_rel_type: relType.trim(),
|
|
76392
|
+
p_author: author,
|
|
76393
|
+
p_author_type: authorType
|
|
76394
|
+
});
|
|
76395
|
+
if (!removed) {
|
|
76396
|
+
println(c.dim("(no such relation — nothing removed)"));
|
|
76397
|
+
return;
|
|
76398
|
+
}
|
|
76399
|
+
println(c.green("✓ ") + `Removed ${removed} relation row(s).`);
|
|
76400
|
+
println(c.dim(" Lifecycle status left as-is — a document may be superseded by something else."));
|
|
76401
|
+
});
|
|
76402
|
+
}
|
|
76403
|
+
function registerRelationList(program2) {
|
|
76404
|
+
program2.command("list <document-id>").description("Show every relation touching a document (both directions).").option("--json", "Emit machine-readable JSON.").action(async (documentId, options) => {
|
|
76405
|
+
const id = requireUuid(documentId, "document-id");
|
|
76406
|
+
const client = getClient();
|
|
76407
|
+
const rows = await client.rpc("cerefox_get_relations", {
|
|
76408
|
+
p_document_id: id
|
|
76409
|
+
}) ?? [];
|
|
76410
|
+
if (options.json) {
|
|
76411
|
+
printJson(rows);
|
|
76412
|
+
return;
|
|
76413
|
+
}
|
|
76414
|
+
if (rows.length === 0) {
|
|
76415
|
+
println(c.dim("(no relations)"));
|
|
76416
|
+
return;
|
|
76417
|
+
}
|
|
76418
|
+
for (const r of rows) {
|
|
76419
|
+
const arrow = r.direction === "outbound" ? "→" : "←";
|
|
76420
|
+
const life = r.other_lifecycle && r.other_lifecycle !== "active" ? c.yellow(` [${r.other_lifecycle}]`) : "";
|
|
76421
|
+
println(` ${arrow} ${c.bold(r.rel_type)} ${r.other_title}${life} ${c.dim(`[id: ${r.other_id}]`)}`);
|
|
76422
|
+
}
|
|
76423
|
+
});
|
|
76424
|
+
}
|
|
76425
|
+
function registerRelationNeighbors(program2) {
|
|
76426
|
+
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) => {
|
|
76427
|
+
const id = requireUuid(documentId, "document-id");
|
|
76428
|
+
const depth = Number.parseInt(options.depth ?? "1", 10);
|
|
76429
|
+
const limit = Number.parseInt(options.limit ?? "50", 10);
|
|
76430
|
+
if (Number.isNaN(depth) || depth < 1 || depth > 5) {
|
|
76431
|
+
throw userError("--depth must be between 1 and 5.");
|
|
76432
|
+
}
|
|
76433
|
+
const client = getClient();
|
|
76434
|
+
const rows = await client.rpc("cerefox_get_neighbors", {
|
|
76435
|
+
p_document_id: id,
|
|
76436
|
+
p_rel_type: relType.trim(),
|
|
76437
|
+
p_depth: depth,
|
|
76438
|
+
p_limit: Number.isNaN(limit) ? 50 : limit
|
|
76439
|
+
}) ?? [];
|
|
76440
|
+
if (options.json) {
|
|
76441
|
+
printJson(rows);
|
|
76442
|
+
return;
|
|
76443
|
+
}
|
|
76444
|
+
if (rows.length === 0) {
|
|
76445
|
+
println(c.dim(`(nothing reachable via "${relType.trim()}")`));
|
|
76446
|
+
return;
|
|
76447
|
+
}
|
|
76448
|
+
for (const r of rows) {
|
|
76449
|
+
const life = r.lifecycle_status && r.lifecycle_status !== "active" ? c.yellow(` [${r.lifecycle_status}]`) : "";
|
|
76450
|
+
println(` ${c.dim(`depth ${r.depth}`)} ${r.direction === "outbound" ? "→" : "←"} ` + `${r.title}${life} ${c.dim(`[id: ${r.document_id}]`)}`);
|
|
76451
|
+
}
|
|
76452
|
+
});
|
|
76453
|
+
}
|
|
76454
|
+
|
|
75741
76455
|
// src/cli/commands/get-doc.ts
|
|
75742
76456
|
init_cli_core();
|
|
75743
76457
|
init_cli_core();
|
|
@@ -77554,6 +78268,10 @@ async function action29(target, options) {
|
|
|
77554
78268
|
} else {
|
|
77555
78269
|
warn("This backup predates project-membership capture (format 1) — documents " + "will be restored WITHOUT their project assignments.");
|
|
77556
78270
|
}
|
|
78271
|
+
const trashedInFile = payload.documents.filter((d) => d.deleted_at != null).length;
|
|
78272
|
+
if (trashedInFile > 0) {
|
|
78273
|
+
println(c.dim(` trashed documents in file: ${trashedInFile} — restored as trash ` + "(still deleted; recover with `cerefox document restore`)"));
|
|
78274
|
+
}
|
|
77557
78275
|
println("");
|
|
77558
78276
|
const client = getClient();
|
|
77559
78277
|
let restored = 0;
|
|
@@ -77561,7 +78279,7 @@ async function action29(target, options) {
|
|
|
77561
78279
|
let errors4 = 0;
|
|
77562
78280
|
const errorDetails = [];
|
|
77563
78281
|
for (const doc2 of payload.documents) {
|
|
77564
|
-
const { data: existing } = await client.raw.from("cerefox_documents").select("id").eq
|
|
78282
|
+
const { data: existing } = await client.raw.from("cerefox_documents").select("id").or(`id.eq.${doc2.id},content_hash.eq.${doc2.content_hash}`).limit(1).maybeSingle();
|
|
77565
78283
|
if (existing) {
|
|
77566
78284
|
skipped++;
|
|
77567
78285
|
continue;
|
|
@@ -77590,6 +78308,7 @@ async function action29(target, options) {
|
|
|
77590
78308
|
}
|
|
77591
78309
|
let projectsRestored = 0;
|
|
77592
78310
|
let membershipsRestored = 0;
|
|
78311
|
+
let relationsRestored = 0;
|
|
77593
78312
|
if (!options.dryRun && hasMemberships) {
|
|
77594
78313
|
const projects = payload.projects ?? [];
|
|
77595
78314
|
if (projects.length > 0) {
|
|
@@ -77623,11 +78342,33 @@ async function action29(target, options) {
|
|
|
77623
78342
|
}
|
|
77624
78343
|
membershipsRestored += links.slice(i, i + 500).length;
|
|
77625
78344
|
}
|
|
78345
|
+
const relations = (payload.relations ?? []).filter((r) => presentDocIds.has(r.source_id) && presentDocIds.has(r.target_id));
|
|
78346
|
+
if (relations.length > 0) {
|
|
78347
|
+
for (let i = 0;i < relations.length; i += 500) {
|
|
78348
|
+
const { error: relErr } = await client.raw.from("cerefox_document_relations").upsert(relations.slice(i, i + 500), {
|
|
78349
|
+
onConflict: "source_id,target_id,rel_type",
|
|
78350
|
+
ignoreDuplicates: true
|
|
78351
|
+
});
|
|
78352
|
+
if (relErr) {
|
|
78353
|
+
errorDetails.push({ title: "(relations)", error: relErr.message });
|
|
78354
|
+
errors4++;
|
|
78355
|
+
break;
|
|
78356
|
+
}
|
|
78357
|
+
relationsRestored += relations.slice(i, i + 500).length;
|
|
78358
|
+
}
|
|
78359
|
+
const dropped = (payload.relations?.length ?? 0) - relations.length;
|
|
78360
|
+
if (dropped > 0) {
|
|
78361
|
+
warn(`${dropped} relation(s) skipped — one or both documents were not restored.`);
|
|
78362
|
+
}
|
|
78363
|
+
}
|
|
77626
78364
|
}
|
|
77627
78365
|
println("");
|
|
77628
78366
|
println((options.dryRun ? c.yellow("(dry-run) ") : "") + c.bold(`Summary: ${restored} restored · ${skipped} skipped · ${errors4} errors`));
|
|
77629
78367
|
if (hasMemberships && !options.dryRun) {
|
|
77630
|
-
println(c.dim(` projects: ${projectsRestored} · memberships: ${membershipsRestored}`));
|
|
78368
|
+
println(c.dim(` projects: ${projectsRestored} · memberships: ${membershipsRestored}` + (relationsRestored > 0 ? ` · relations: ${relationsRestored}` : "")));
|
|
78369
|
+
}
|
|
78370
|
+
if (trashedInFile > 0) {
|
|
78371
|
+
println(c.dim(` ${trashedInFile} of those are trashed and stay trashed.`));
|
|
77631
78372
|
}
|
|
77632
78373
|
if (errors4 > 0) {
|
|
77633
78374
|
println("");
|
|
@@ -77666,8 +78407,10 @@ async function action30(query, options) {
|
|
|
77666
78407
|
throw userError("Empty query.");
|
|
77667
78408
|
}
|
|
77668
78409
|
const matchCount = parsePositiveInt(options.matchCount, "--match-count", 5);
|
|
77669
|
-
const
|
|
77670
|
-
const
|
|
78410
|
+
const envAlpha = getConfiguredSearchAlpha();
|
|
78411
|
+
const alphaParam = options.alpha !== undefined ? { p_alpha: parseFloat01(options.alpha, "--alpha", envAlpha ?? 0.7) } : envAlpha !== undefined ? { p_alpha: envAlpha } : {};
|
|
78412
|
+
const envScore = getConfiguredMinSearchScore();
|
|
78413
|
+
const scoreParam = options.minScore !== undefined ? { p_min_score: parseFloat01(options.minScore, "--min-score", envScore ?? 0.5) } : envScore !== undefined ? { p_min_score: envScore } : {};
|
|
77671
78414
|
const envCoverage = getMinTermCoverage();
|
|
77672
78415
|
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 } : {};
|
|
77673
78416
|
const maxBytes = parseNonNegativeInt(options.maxBytes, "--max-bytes", getMaxResponseBytes());
|
|
@@ -77709,10 +78452,10 @@ async function action30(query, options) {
|
|
|
77709
78452
|
p_query_text: query,
|
|
77710
78453
|
p_query_embedding: embedding,
|
|
77711
78454
|
p_match_count: matchCount,
|
|
77712
|
-
p_alpha: alpha,
|
|
77713
78455
|
p_use_upgrade: false,
|
|
77714
78456
|
p_project_id: projectId,
|
|
77715
|
-
|
|
78457
|
+
...alphaParam,
|
|
78458
|
+
...scoreParam,
|
|
77716
78459
|
...metaFilterParam,
|
|
77717
78460
|
...coverageParam
|
|
77718
78461
|
};
|
|
@@ -77722,9 +78465,9 @@ async function action30(query, options) {
|
|
|
77722
78465
|
p_query_text: query,
|
|
77723
78466
|
p_query_embedding: embedding,
|
|
77724
78467
|
p_match_count: matchCount,
|
|
77725
|
-
p_alpha: alpha,
|
|
77726
78468
|
p_project_id: projectId,
|
|
77727
|
-
|
|
78469
|
+
...alphaParam,
|
|
78470
|
+
...scoreParam,
|
|
77728
78471
|
...metaFilterParam,
|
|
77729
78472
|
...coverageParam
|
|
77730
78473
|
};
|
|
@@ -77908,7 +78651,8 @@ async function action31(options) {
|
|
|
77908
78651
|
stdio: "inherit"
|
|
77909
78652
|
});
|
|
77910
78653
|
if (result.status !== 0) {
|
|
77911
|
-
|
|
78654
|
+
const notFound2 = result.error?.code === "ENOENT";
|
|
78655
|
+
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(" ")}\``);
|
|
77912
78656
|
}
|
|
77913
78657
|
println("");
|
|
77914
78658
|
println(c.green(`✓ Upgraded to ${target}.`));
|
|
@@ -81626,7 +82370,7 @@ function logWebUsage(ctx, params) {
|
|
|
81626
82370
|
}
|
|
81627
82371
|
|
|
81628
82372
|
// src/web/routes/discovery.ts
|
|
81629
|
-
var
|
|
82373
|
+
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
81630
82374
|
var DOC_COLS = "id, title, source, source_path, content_hash, metadata, chunk_count, total_chars, review_status, created_at, updated_at, deleted_at";
|
|
81631
82375
|
function jsonByteLength(value) {
|
|
81632
82376
|
return Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
@@ -82098,7 +82842,7 @@ function registerDiscoveryRoutes(app, ctx) {
|
|
|
82098
82842
|
if (!path) {
|
|
82099
82843
|
return c2.json({ tried_path: "", anchor, matches: [] });
|
|
82100
82844
|
}
|
|
82101
|
-
if (
|
|
82845
|
+
if (UUID_RE4.test(path)) {
|
|
82102
82846
|
if (path === fromDocId) {
|
|
82103
82847
|
return c2.json({ tried_path: path, anchor, matches: [] });
|
|
82104
82848
|
}
|
|
@@ -83260,7 +84004,13 @@ import {
|
|
|
83260
84004
|
} from "node:fs";
|
|
83261
84005
|
import { homedir as homedir9 } from "node:os";
|
|
83262
84006
|
import { join as join18 } from "node:path";
|
|
83263
|
-
|
|
84007
|
+
function resolveStateDir(override = process.env.CEREFOX_CONFIG_DIR, home = homedir9()) {
|
|
84008
|
+
override = (override ?? "").trim();
|
|
84009
|
+
if (!override)
|
|
84010
|
+
return join18(home, ".cerefox");
|
|
84011
|
+
return override === "~" || override.startsWith("~/") ? join18(home, override.slice(2)) : override;
|
|
84012
|
+
}
|
|
84013
|
+
var STATE_DIR = resolveStateDir();
|
|
83264
84014
|
var PID_FILE = join18(STATE_DIR, "web.pid");
|
|
83265
84015
|
var LOG_FILE = join18(STATE_DIR, "web.log");
|
|
83266
84016
|
var daemonPaths = { stateDir: STATE_DIR, pidFile: PID_FILE, logFile: LOG_FILE };
|
|
@@ -83607,6 +84357,11 @@ Learn more:
|
|
|
83607
84357
|
const metadata = program2.command("metadata").description("Metadata: keys, search.");
|
|
83608
84358
|
moveInto(metadata, registerListMetadataKeys, "keys");
|
|
83609
84359
|
moveInto(metadata, registerMetadataSearch, "search");
|
|
84360
|
+
const relation = program2.command("relation").description("Document relations: set, delete, list, neighbors.");
|
|
84361
|
+
registerRelationSet(relation);
|
|
84362
|
+
registerRelationDelete(relation);
|
|
84363
|
+
registerRelationList(relation);
|
|
84364
|
+
registerRelationNeighbors(relation);
|
|
83610
84365
|
const audit = program2.command("audit").description("Audit log: list.");
|
|
83611
84366
|
moveInto(audit, registerGetAuditLog, "list");
|
|
83612
84367
|
const config2 = program2.command("config").description("Runtime config: list, get, set.");
|