rulesync 8.15.0 → 8.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -473,7 +473,8 @@ function validateOutputRoot(outputRoot) {
473
473
  throw new Error("outputRoot cannot be an empty string");
474
474
  }
475
475
  if (isAbsolute(outputRoot)) {
476
- const segments = outputRoot.split(/[/\\]/);
476
+ const separatorRegex = process.platform === "win32" ? /[/\\]/ : /\//;
477
+ const segments = outputRoot.split(separatorRegex);
477
478
  if (segments.includes("..")) {
478
479
  throw new Error(`Path traversal detected: ${outputRoot}`);
479
480
  }
@@ -4825,7 +4826,9 @@ async function buildCodexConfigTomlContent({
4825
4826
  if (typeof configToml.features !== "object" || configToml.features === null) {
4826
4827
  configToml.features = {};
4827
4828
  }
4828
- configToml.features.codex_hooks = true;
4829
+ const features = configToml.features;
4830
+ delete features.codex_hooks;
4831
+ features.hooks = true;
4829
4832
  return smolToml2.stringify(configToml);
4830
4833
  }
4831
4834
  var CodexcliConfigToml = class _CodexcliConfigToml extends ToolFile {
@@ -10092,6 +10095,7 @@ var ToolPermissions = class extends ToolFile {
10092
10095
  };
10093
10096
 
10094
10097
  // src/features/permissions/augmentcode-permissions.ts
10098
+ var moduleLogger = new ConsoleLogger();
10095
10099
  var AugmentPermissionTypeSchema = z30.enum(["allow", "deny", "ask-user"]);
10096
10100
  var AugmentToolPermissionSchema = z30.looseObject({
10097
10101
  toolName: z30.string(),
@@ -10183,6 +10187,33 @@ function shellRegexToGlob(regex) {
10183
10187
  }
10184
10188
  return glob;
10185
10189
  }
10190
+ function isShellRegexRoundtrippable(regex) {
10191
+ if (!regex.startsWith("^") || !regex.endsWith("$")) {
10192
+ return false;
10193
+ }
10194
+ const body = regex.slice(1, -1);
10195
+ let i = 0;
10196
+ while (i < body.length) {
10197
+ const ch = body[i];
10198
+ if (ch === "\\" && i + 1 < body.length) {
10199
+ i += 2;
10200
+ continue;
10201
+ }
10202
+ if (ch === "." && body[i + 1] === "*") {
10203
+ i += 2;
10204
+ continue;
10205
+ }
10206
+ if (ch === ".") {
10207
+ i += 1;
10208
+ continue;
10209
+ }
10210
+ if (/[$^|+?*(){}[\]]/.test(ch ?? "")) {
10211
+ return false;
10212
+ }
10213
+ i += 1;
10214
+ }
10215
+ return true;
10216
+ }
10186
10217
  var MANAGED_AUGMENT_TOOL_NAMES = new Set(Object.values(CANONICAL_TO_AUGMENT_TOOL_NAMES));
10187
10218
  var AugmentcodePermissions = class _AugmentcodePermissions extends ToolPermissions {
10188
10219
  constructor(params) {
@@ -10190,6 +10221,12 @@ var AugmentcodePermissions = class _AugmentcodePermissions extends ToolPermissio
10190
10221
  ...params,
10191
10222
  fileContent: params.fileContent ?? "{}"
10192
10223
  });
10224
+ if (params.validate) {
10225
+ const result = this.validate();
10226
+ if (!result.success) {
10227
+ throw result.error;
10228
+ }
10229
+ }
10193
10230
  }
10194
10231
  isDeletable() {
10195
10232
  return false;
@@ -10283,14 +10320,27 @@ var AugmentcodePermissions = class _AugmentcodePermissions extends ToolPermissio
10283
10320
  );
10284
10321
  }
10285
10322
  const config = convertAugmentToRulesyncPermissions({
10286
- entries: settings.toolPermissions ?? []
10323
+ entries: settings.toolPermissions ?? [],
10324
+ logger: moduleLogger
10287
10325
  });
10288
10326
  return this.toRulesyncPermissionsDefault({
10289
10327
  fileContent: JSON.stringify(config, null, 2)
10290
10328
  });
10291
10329
  }
10292
10330
  validate() {
10293
- return { success: true, error: null };
10331
+ try {
10332
+ const parsed = JSON.parse(this.fileContent || "{}");
10333
+ const result = AugmentSettingsSchema.safeParse(parsed);
10334
+ if (!result.success) {
10335
+ return { success: false, error: result.error };
10336
+ }
10337
+ return { success: true, error: null };
10338
+ } catch (error) {
10339
+ return {
10340
+ success: false,
10341
+ error: new Error(`Failed to parse AugmentCode permissions JSON: ${formatError(error)}`)
10342
+ };
10343
+ }
10294
10344
  }
10295
10345
  static forDeletion({
10296
10346
  outputRoot = process.cwd(),
@@ -10388,7 +10438,8 @@ function sortAugmentEntries(entries) {
10388
10438
  return decorated.map((d) => d.entry);
10389
10439
  }
10390
10440
  function convertAugmentToRulesyncPermissions({
10391
- entries
10441
+ entries,
10442
+ logger
10392
10443
  }) {
10393
10444
  const actionPriority = {
10394
10445
  deny: 2,
@@ -10399,7 +10450,27 @@ function convertAugmentToRulesyncPermissions({
10399
10450
  for (const entry of entries) {
10400
10451
  const canonical = toCanonicalToolName(entry.toolName);
10401
10452
  const action = augmentTypeToAction(entry.permission.type);
10402
- const pattern = entry.toolName === "launch-process" && entry.shellInputRegex ? shellRegexToGlob(entry.shellInputRegex) : "*";
10453
+ let pattern;
10454
+ if (entry.toolName === "launch-process" && entry.shellInputRegex) {
10455
+ const regex = entry.shellInputRegex;
10456
+ if (isShellRegexRoundtrippable(regex)) {
10457
+ pattern = shellRegexToGlob(regex);
10458
+ } else {
10459
+ if (action === "deny") {
10460
+ logger?.warn(
10461
+ `AugmentCode permissions: shellInputRegex '${regex}' on tool '${entry.toolName}' is not faithfully roundtrippable to a glob. Importing as the catch-all '*' pattern (fail-closed) so the deny rule cannot be silently narrowed on regenerate.`
10462
+ );
10463
+ pattern = "*";
10464
+ } else {
10465
+ pattern = shellRegexToGlob(regex);
10466
+ logger?.warn(
10467
+ `AugmentCode permissions: shellInputRegex '${regex}' on tool '${entry.toolName}' is not faithfully roundtrippable to a glob. Importing as glob '${pattern}'; the rule may match a different set of inputs after regenerate.`
10468
+ );
10469
+ }
10470
+ }
10471
+ } else {
10472
+ pattern = "*";
10473
+ }
10403
10474
  if (!permission[canonical]) {
10404
10475
  permission[canonical] = {};
10405
10476
  }
@@ -10654,6 +10725,12 @@ var ClinePermissions = class _ClinePermissions extends ToolPermissions {
10654
10725
  ...params,
10655
10726
  fileContent: params.fileContent ?? "{}"
10656
10727
  });
10728
+ if (params.validate) {
10729
+ const result = this.validate();
10730
+ if (!result.success) {
10731
+ throw result.error;
10732
+ }
10733
+ }
10657
10734
  }
10658
10735
  isDeletable() {
10659
10736
  return false;
@@ -10792,7 +10869,19 @@ var ClinePermissions = class _ClinePermissions extends ToolPermissions {
10792
10869
  });
10793
10870
  }
10794
10871
  validate() {
10795
- return { success: true, error: null };
10872
+ try {
10873
+ const parsed = JSON.parse(this.fileContent || "{}");
10874
+ const result = ClineCommandPermissionsSchema.safeParse(parsed);
10875
+ if (!result.success) {
10876
+ return { success: false, error: result.error };
10877
+ }
10878
+ return { success: true, error: null };
10879
+ } catch (error) {
10880
+ return {
10881
+ success: false,
10882
+ error: new Error(`Failed to parse Cline permissions JSON: ${formatError(error)}`)
10883
+ };
10884
+ }
10796
10885
  }
10797
10886
  static forDeletion({
10798
10887
  outputRoot = process.cwd(),
@@ -10810,10 +10899,12 @@ var ClinePermissions = class _ClinePermissions extends ToolPermissions {
10810
10899
  };
10811
10900
 
10812
10901
  // src/features/permissions/codexcli-permissions.ts
10813
- import { join as join68 } from "path";
10902
+ import { isAbsolute as isAbsolute3, join as join68 } from "path";
10814
10903
  import * as smolToml4 from "smol-toml";
10815
10904
  var RULESYNC_PROFILE_NAME = "rulesync";
10816
10905
  var RULESYNC_BASH_RULES_FILE_NAME = "rulesync.rules";
10906
+ var CODEX_PROJECT_ROOTS_KEY = ":project_roots";
10907
+ var CODEX_GLOB_SCAN_MAX_DEPTH = 8;
10817
10908
  var CodexcliPermissions = class _CodexcliPermissions extends ToolPermissions {
10818
10909
  static getSettablePaths(_options = {}) {
10819
10910
  return {
@@ -10924,17 +11015,28 @@ function convertRulesyncToCodexProfile({
10924
11015
  logger
10925
11016
  }) {
10926
11017
  const filesystem = {};
11018
+ const projectRootFilesystem = {};
10927
11019
  const domains = {};
10928
11020
  for (const [toolName, rules] of Object.entries(config.permission)) {
10929
11021
  if (toolName === "read") {
10930
11022
  for (const [pattern, action] of Object.entries(rules)) {
10931
- filesystem[pattern] = mapReadAction(action);
11023
+ addFilesystemRule({
11024
+ filesystem,
11025
+ projectRootFilesystem,
11026
+ pattern,
11027
+ access: mapReadAction(action)
11028
+ });
10932
11029
  }
10933
11030
  continue;
10934
11031
  }
10935
11032
  if (toolName === "edit" || toolName === "write") {
10936
11033
  for (const [pattern, action] of Object.entries(rules)) {
10937
- filesystem[pattern] = mapWriteAction(action);
11034
+ addFilesystemRule({
11035
+ filesystem,
11036
+ projectRootFilesystem,
11037
+ pattern,
11038
+ access: mapWriteAction(action)
11039
+ });
10938
11040
  }
10939
11041
  continue;
10940
11042
  }
@@ -10954,6 +11056,12 @@ function convertRulesyncToCodexProfile({
10954
11056
  `Codex CLI permissions support only read/edit/write/webfetch categories. Skipping: ${toolName}`
10955
11057
  );
10956
11058
  }
11059
+ if (Object.keys(projectRootFilesystem).length > 0) {
11060
+ if (Object.keys(projectRootFilesystem).some((pattern) => pattern.includes("**"))) {
11061
+ filesystem.glob_scan_max_depth = CODEX_GLOB_SCAN_MAX_DEPTH;
11062
+ }
11063
+ filesystem[CODEX_PROJECT_ROOTS_KEY] = projectRootFilesystem;
11064
+ }
10957
11065
  return {
10958
11066
  ...Object.keys(filesystem).length > 0 ? { filesystem } : {},
10959
11067
  ...Object.keys(domains).length > 0 ? { network: { domains } } : {}
@@ -10965,13 +11073,14 @@ function convertCodexProfileToRulesync(profile) {
10965
11073
  permission.read = {};
10966
11074
  permission.edit = {};
10967
11075
  for (const [pattern, access] of Object.entries(profile.filesystem)) {
10968
- if (access === "none") {
10969
- permission.read[pattern] = "deny";
10970
- permission.edit[pattern] = "deny";
10971
- } else if (access === "read") {
10972
- permission.read[pattern] = "allow";
10973
- } else {
10974
- permission.edit[pattern] = "allow";
11076
+ if (isCodexFilesystemAccess(access)) {
11077
+ addRulesyncFilesystemRule(permission, pattern, access);
11078
+ continue;
11079
+ }
11080
+ if (isCodexFilesystemRuleTable(access)) {
11081
+ for (const [nestedPattern, nestedAccess] of Object.entries(access)) {
11082
+ addRulesyncFilesystemRule(permission, nestedPattern, nestedAccess);
11083
+ }
10975
11084
  }
10976
11085
  }
10977
11086
  }
@@ -10994,6 +11103,35 @@ function toCodexProfile(value) {
10994
11103
  ...domains ? { network: { domains } } : {}
10995
11104
  };
10996
11105
  }
11106
+ function addFilesystemRule({
11107
+ filesystem,
11108
+ projectRootFilesystem,
11109
+ pattern,
11110
+ access
11111
+ }) {
11112
+ if (canBeCodexFilesystemRoot(pattern)) {
11113
+ filesystem[pattern] = access;
11114
+ return;
11115
+ }
11116
+ projectRootFilesystem[pattern] = access;
11117
+ }
11118
+ function canBeCodexFilesystemRoot(pattern) {
11119
+ return isAbsolute3(pattern) || /^[A-Za-z]:[\\/]/.test(pattern) || pattern.startsWith("~/") || pattern === "~" || pattern.startsWith(":");
11120
+ }
11121
+ function addRulesyncFilesystemRule(permission, pattern, access) {
11122
+ if (access === "none") {
11123
+ permission.read ??= {};
11124
+ permission.edit ??= {};
11125
+ permission.read[pattern] = "deny";
11126
+ permission.edit[pattern] = "deny";
11127
+ } else if (access === "read") {
11128
+ permission.read ??= {};
11129
+ permission.read[pattern] = "allow";
11130
+ } else {
11131
+ permission.edit ??= {};
11132
+ permission.edit[pattern] = "allow";
11133
+ }
11134
+ }
10997
11135
  function toMutableTable(value) {
10998
11136
  if (!value || typeof value !== "object" || Array.isArray(value)) {
10999
11137
  return {};
@@ -11004,8 +11142,33 @@ function toFilesystemRecord(value) {
11004
11142
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
11005
11143
  const result = {};
11006
11144
  for (const [key, raw] of Object.entries(value)) {
11007
- if (typeof raw !== "string") continue;
11008
- if (raw === "read" || raw === "write" || raw === "none") {
11145
+ if (isCodexFilesystemAccess(raw)) {
11146
+ result[key] = raw;
11147
+ continue;
11148
+ }
11149
+ if (key === "glob_scan_max_depth" && typeof raw === "number") {
11150
+ result[key] = raw;
11151
+ continue;
11152
+ }
11153
+ const nested = toCodexFilesystemRuleTable(raw);
11154
+ if (nested) {
11155
+ result[key] = nested;
11156
+ }
11157
+ }
11158
+ return Object.keys(result).length > 0 ? result : void 0;
11159
+ }
11160
+ function isCodexFilesystemAccess(value) {
11161
+ return value === "read" || value === "write" || value === "none";
11162
+ }
11163
+ function isCodexFilesystemRuleTable(value) {
11164
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
11165
+ return Object.values(value).every(isCodexFilesystemAccess);
11166
+ }
11167
+ function toCodexFilesystemRuleTable(value) {
11168
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
11169
+ const result = {};
11170
+ for (const [key, raw] of Object.entries(value)) {
11171
+ if (isCodexFilesystemAccess(raw)) {
11009
11172
  result[key] = raw;
11010
11173
  }
11011
11174
  }
@@ -11385,7 +11548,7 @@ var RESERVED_OBJECT_KEYS = /* @__PURE__ */ new Set([
11385
11548
  "constructor",
11386
11549
  "prototype"
11387
11550
  ]);
11388
- var moduleLogger = new ConsoleLogger();
11551
+ var moduleLogger2 = new ConsoleLogger();
11389
11552
  var GeminicliPermissions = class _GeminicliPermissions extends ToolPermissions {
11390
11553
  static getSettablePaths(_options = {}) {
11391
11554
  return {
@@ -11414,7 +11577,7 @@ var GeminicliPermissions = class _GeminicliPermissions extends ToolPermissions {
11414
11577
  rulesyncPermissions,
11415
11578
  validate = true,
11416
11579
  global = false,
11417
- logger = moduleLogger
11580
+ logger = moduleLogger2
11418
11581
  }) {
11419
11582
  const paths = this.getSettablePaths({ global });
11420
11583
  const fileContent = buildGeminicliPolicyContent(rulesyncPermissions.getJson(), logger);
@@ -11439,31 +11602,31 @@ var GeminicliPermissions = class _GeminicliPermissions extends ToolPermissions {
11439
11602
  { cause: error }
11440
11603
  );
11441
11604
  }
11442
- const rules = extractRules(parsed, moduleLogger);
11605
+ const rules = extractRules(parsed, moduleLogger2);
11443
11606
  for (const [index, rule] of rules.entries()) {
11444
11607
  const mappedCategory = Object.hasOwn(GEMINICLI_TO_RULESYNC_TOOL_NAME, rule.toolName) ? GEMINICLI_TO_RULESYNC_TOOL_NAME[rule.toolName] : void 0;
11445
11608
  const category = mappedCategory ?? rule.toolName;
11446
11609
  if (RESERVED_OBJECT_KEYS.has(category)) {
11447
- moduleLogger.warn(
11610
+ moduleLogger2.warn(
11448
11611
  `Skipping rule #${index} in ${this.getRelativeFilePath()}: toolName "${rule.toolName}" maps to a reserved object key ("${category}") and would risk prototype pollution.`
11449
11612
  );
11450
11613
  continue;
11451
11614
  }
11452
11615
  const action = mapFromGeminicliDecision(rule.decision);
11453
11616
  if (!action) {
11454
- moduleLogger.warn(
11617
+ moduleLogger2.warn(
11455
11618
  `Skipping rule #${index} (toolName="${rule.toolName}", commandPrefix=${JSON.stringify(rule.commandPrefix)}, argsPattern=${JSON.stringify(rule.argsPattern)}) in ${this.getRelativeFilePath()}: unknown decision ${JSON.stringify(rule.decision)}`
11456
11619
  );
11457
11620
  continue;
11458
11621
  }
11459
11622
  if (rule.toolName === "run_shell_command" && rule.commandPrefix !== void 0 && rule.argsPattern !== void 0) {
11460
- moduleLogger.warn(
11623
+ moduleLogger2.warn(
11461
11624
  `Rule #${index} in ${this.getRelativeFilePath()} sets both commandPrefix and argsPattern; rulesync will honor argsPattern and ignore commandPrefix=${JSON.stringify(rule.commandPrefix)}.`
11462
11625
  );
11463
11626
  }
11464
11627
  const pattern = extractPattern(rule);
11465
11628
  if (RESERVED_OBJECT_KEYS.has(pattern)) {
11466
- moduleLogger.warn(
11629
+ moduleLogger2.warn(
11467
11630
  `Skipping rule #${index} in ${this.getRelativeFilePath()}: pattern "${pattern}" is a reserved object key.`
11468
11631
  );
11469
11632
  continue;
@@ -12232,7 +12395,7 @@ var QwenSettingsPermissionsSchema = z36.looseObject({
12232
12395
  var QwenSettingsSchema = z36.looseObject({
12233
12396
  permissions: z36.optional(QwenSettingsPermissionsSchema)
12234
12397
  });
12235
- var moduleLogger2 = new ConsoleLogger();
12398
+ var moduleLogger3 = new ConsoleLogger();
12236
12399
  var CANONICAL_TO_QWEN_TOOL_NAMES = {
12237
12400
  bash: "Bash",
12238
12401
  read: "Read",
@@ -12287,6 +12450,12 @@ var QwencodePermissions = class _QwencodePermissions extends ToolPermissions {
12287
12450
  ...params,
12288
12451
  fileContent: params.fileContent ?? "{}"
12289
12452
  });
12453
+ if (params.validate) {
12454
+ const result = this.validate();
12455
+ if (!result.success) {
12456
+ throw result.error;
12457
+ }
12458
+ }
12290
12459
  }
12291
12460
  isDeletable() {
12292
12461
  return false;
@@ -12408,7 +12577,19 @@ var QwencodePermissions = class _QwencodePermissions extends ToolPermissions {
12408
12577
  });
12409
12578
  }
12410
12579
  validate() {
12411
- return { success: true, error: null };
12580
+ try {
12581
+ const parsed = JSON.parse(this.fileContent || "{}");
12582
+ const result = QwenSettingsSchema.safeParse(parsed);
12583
+ if (!result.success) {
12584
+ return { success: false, error: result.error };
12585
+ }
12586
+ return { success: true, error: null };
12587
+ } catch (error) {
12588
+ return {
12589
+ success: false,
12590
+ error: new Error(`Failed to parse Qwen permissions JSON: ${formatError(error)}`)
12591
+ };
12592
+ }
12412
12593
  }
12413
12594
  static forDeletion({
12414
12595
  outputRoot = process.cwd(),
@@ -12449,7 +12630,7 @@ function convertRulesyncToQwenPermissions(config) {
12449
12630
  }
12450
12631
  function convertQwenToRulesyncPermissions(params) {
12451
12632
  const permission = {};
12452
- const logger = params.logger ?? moduleLogger2;
12633
+ const logger = params.logger ?? moduleLogger3;
12453
12634
  const processEntries = (entries, action) => {
12454
12635
  for (const entry of entries) {
12455
12636
  const parsed = parseQwenPermissionEntry(entry, { logger });
@@ -13201,7 +13382,8 @@ var RulesyncSkillFrontmatterSchemaInternal = z39.looseObject({
13201
13382
  "allowed-tools": z39.optional(z39.array(z39.string())),
13202
13383
  model: z39.optional(z39.string()),
13203
13384
  "disable-model-invocation": z39.optional(z39.boolean()),
13204
- "scheduled-task": z39.optional(z39.boolean())
13385
+ "scheduled-task": z39.optional(z39.boolean()),
13386
+ paths: z39.optional(z39.union([z39.string(), z39.array(z39.string())]))
13205
13387
  })
13206
13388
  ),
13207
13389
  codexcli: z39.optional(
@@ -13976,7 +14158,8 @@ var ClaudecodeSkillFrontmatterSchema = z43.looseObject({
13976
14158
  description: z43.string(),
13977
14159
  "allowed-tools": z43.optional(z43.array(z43.string())),
13978
14160
  model: z43.optional(z43.string()),
13979
- "disable-model-invocation": z43.optional(z43.boolean())
14161
+ "disable-model-invocation": z43.optional(z43.boolean()),
14162
+ paths: z43.optional(z43.union([z43.string(), z43.array(z43.string())]))
13980
14163
  });
13981
14164
  var ClaudecodeSkill = class _ClaudecodeSkill extends ToolSkill {
13982
14165
  constructor({
@@ -14049,6 +14232,7 @@ var ClaudecodeSkill = class _ClaudecodeSkill extends ToolSkill {
14049
14232
  ...frontmatter["disable-model-invocation"] !== void 0 && {
14050
14233
  "disable-model-invocation": frontmatter["disable-model-invocation"]
14051
14234
  },
14235
+ ...frontmatter.paths !== void 0 && { paths: frontmatter.paths },
14052
14236
  ...this.relativeDirPath === CLAUDE_SCHEDULED_TASKS_DIR_PATH && { "scheduled-task": true }
14053
14237
  };
14054
14238
  const rulesyncFrontmatter = {
@@ -14086,6 +14270,9 @@ var ClaudecodeSkill = class _ClaudecodeSkill extends ToolSkill {
14086
14270
  },
14087
14271
  ...rulesyncFrontmatter.claudecode?.["disable-model-invocation"] !== void 0 && {
14088
14272
  "disable-model-invocation": rulesyncFrontmatter.claudecode["disable-model-invocation"]
14273
+ },
14274
+ ...rulesyncFrontmatter.claudecode?.paths !== void 0 && {
14275
+ paths: rulesyncFrontmatter.claudecode.paths
14089
14276
  }
14090
14277
  };
14091
14278
  const settablePaths = _ClaudecodeSkill.getSettablePaths({ global });
@@ -21030,6 +21217,8 @@ var CopilotRuleFrontmatterSchema = z77.object({
21030
21217
  applyTo: z77.optional(z77.string()),
21031
21218
  excludeAgent: z77.optional(z77.union([z77.literal("code-review"), z77.literal("coding-agent")]))
21032
21219
  });
21220
+ var normalizeRelativePath = (path4) => path4.replace(/\\/g, "/").replace(/\/+/g, "/");
21221
+ var sameRelativePath = (leftDir, leftFile, rightDir, rightFile) => normalizeRelativePath(join133(leftDir, leftFile)) === normalizeRelativePath(join133(rightDir, rightFile));
21033
21222
  var CopilotRule = class _CopilotRule extends ToolRule {
21034
21223
  frontmatter;
21035
21224
  body;
@@ -21147,7 +21336,12 @@ var CopilotRule = class _CopilotRule extends ToolRule {
21147
21336
  global = false
21148
21337
  }) {
21149
21338
  const paths = this.getSettablePaths({ global });
21150
- const isRoot = relativeDirPath ? join133(relativeDirPath, relativeFilePath) === join133(paths.root.relativeDirPath, paths.root.relativeFilePath) : relativeFilePath === paths.root.relativeFilePath;
21339
+ const isRoot = relativeDirPath ? sameRelativePath(
21340
+ relativeDirPath,
21341
+ relativeFilePath,
21342
+ paths.root.relativeDirPath,
21343
+ paths.root.relativeFilePath
21344
+ ) : relativeFilePath === paths.root.relativeFilePath;
21151
21345
  const resolvedRelativeDirPath = relativeDirPath ?? (isRoot ? paths.root.relativeDirPath : paths.nonRoot?.relativeDirPath ?? paths.root.relativeDirPath);
21152
21346
  if (isRoot) {
21153
21347
  const relativePath2 = join133(paths.root.relativeDirPath, paths.root.relativeFilePath);
@@ -21191,7 +21385,12 @@ var CopilotRule = class _CopilotRule extends ToolRule {
21191
21385
  global = false
21192
21386
  }) {
21193
21387
  const paths = this.getSettablePaths({ global });
21194
- const isRoot = join133(relativeDirPath, relativeFilePath) === join133(paths.root.relativeDirPath, paths.root.relativeFilePath);
21388
+ const isRoot = sameRelativePath(
21389
+ relativeDirPath,
21390
+ relativeFilePath,
21391
+ paths.root.relativeDirPath,
21392
+ paths.root.relativeFilePath
21393
+ );
21195
21394
  return new _CopilotRule({
21196
21395
  outputRoot,
21197
21396
  relativeDirPath,