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.
@@ -282,7 +282,8 @@ function validateOutputRoot(outputRoot) {
282
282
  throw new Error("outputRoot cannot be an empty string");
283
283
  }
284
284
  if ((0, import_node_path2.isAbsolute)(outputRoot)) {
285
- const segments = outputRoot.split(/[/\\]/);
285
+ const separatorRegex = process.platform === "win32" ? /[/\\]/ : /\//;
286
+ const segments = outputRoot.split(separatorRegex);
286
287
  if (segments.includes("..")) {
287
288
  throw new Error(`Path traversal detected: ${outputRoot}`);
288
289
  }
@@ -4858,7 +4859,9 @@ async function buildCodexConfigTomlContent({
4858
4859
  if (typeof configToml.features !== "object" || configToml.features === null) {
4859
4860
  configToml.features = {};
4860
4861
  }
4861
- configToml.features.codex_hooks = true;
4862
+ const features = configToml.features;
4863
+ delete features.codex_hooks;
4864
+ features.hooks = true;
4862
4865
  return smolToml2.stringify(configToml);
4863
4866
  }
4864
4867
  var CodexcliConfigToml = class _CodexcliConfigToml extends ToolFile {
@@ -10125,6 +10128,7 @@ var ToolPermissions = class extends ToolFile {
10125
10128
  };
10126
10129
 
10127
10130
  // src/features/permissions/augmentcode-permissions.ts
10131
+ var moduleLogger = new ConsoleLogger();
10128
10132
  var AugmentPermissionTypeSchema = import_mini30.z.enum(["allow", "deny", "ask-user"]);
10129
10133
  var AugmentToolPermissionSchema = import_mini30.z.looseObject({
10130
10134
  toolName: import_mini30.z.string(),
@@ -10216,6 +10220,33 @@ function shellRegexToGlob(regex) {
10216
10220
  }
10217
10221
  return glob;
10218
10222
  }
10223
+ function isShellRegexRoundtrippable(regex) {
10224
+ if (!regex.startsWith("^") || !regex.endsWith("$")) {
10225
+ return false;
10226
+ }
10227
+ const body = regex.slice(1, -1);
10228
+ let i = 0;
10229
+ while (i < body.length) {
10230
+ const ch = body[i];
10231
+ if (ch === "\\" && i + 1 < body.length) {
10232
+ i += 2;
10233
+ continue;
10234
+ }
10235
+ if (ch === "." && body[i + 1] === "*") {
10236
+ i += 2;
10237
+ continue;
10238
+ }
10239
+ if (ch === ".") {
10240
+ i += 1;
10241
+ continue;
10242
+ }
10243
+ if (/[$^|+?*(){}[\]]/.test(ch ?? "")) {
10244
+ return false;
10245
+ }
10246
+ i += 1;
10247
+ }
10248
+ return true;
10249
+ }
10219
10250
  var MANAGED_AUGMENT_TOOL_NAMES = new Set(Object.values(CANONICAL_TO_AUGMENT_TOOL_NAMES));
10220
10251
  var AugmentcodePermissions = class _AugmentcodePermissions extends ToolPermissions {
10221
10252
  constructor(params) {
@@ -10223,6 +10254,12 @@ var AugmentcodePermissions = class _AugmentcodePermissions extends ToolPermissio
10223
10254
  ...params,
10224
10255
  fileContent: params.fileContent ?? "{}"
10225
10256
  });
10257
+ if (params.validate) {
10258
+ const result = this.validate();
10259
+ if (!result.success) {
10260
+ throw result.error;
10261
+ }
10262
+ }
10226
10263
  }
10227
10264
  isDeletable() {
10228
10265
  return false;
@@ -10316,14 +10353,27 @@ var AugmentcodePermissions = class _AugmentcodePermissions extends ToolPermissio
10316
10353
  );
10317
10354
  }
10318
10355
  const config = convertAugmentToRulesyncPermissions({
10319
- entries: settings.toolPermissions ?? []
10356
+ entries: settings.toolPermissions ?? [],
10357
+ logger: moduleLogger
10320
10358
  });
10321
10359
  return this.toRulesyncPermissionsDefault({
10322
10360
  fileContent: JSON.stringify(config, null, 2)
10323
10361
  });
10324
10362
  }
10325
10363
  validate() {
10326
- return { success: true, error: null };
10364
+ try {
10365
+ const parsed = JSON.parse(this.fileContent || "{}");
10366
+ const result = AugmentSettingsSchema.safeParse(parsed);
10367
+ if (!result.success) {
10368
+ return { success: false, error: result.error };
10369
+ }
10370
+ return { success: true, error: null };
10371
+ } catch (error) {
10372
+ return {
10373
+ success: false,
10374
+ error: new Error(`Failed to parse AugmentCode permissions JSON: ${formatError(error)}`)
10375
+ };
10376
+ }
10327
10377
  }
10328
10378
  static forDeletion({
10329
10379
  outputRoot = process.cwd(),
@@ -10421,7 +10471,8 @@ function sortAugmentEntries(entries) {
10421
10471
  return decorated.map((d) => d.entry);
10422
10472
  }
10423
10473
  function convertAugmentToRulesyncPermissions({
10424
- entries
10474
+ entries,
10475
+ logger: logger5
10425
10476
  }) {
10426
10477
  const actionPriority = {
10427
10478
  deny: 2,
@@ -10432,7 +10483,27 @@ function convertAugmentToRulesyncPermissions({
10432
10483
  for (const entry of entries) {
10433
10484
  const canonical = toCanonicalToolName(entry.toolName);
10434
10485
  const action = augmentTypeToAction(entry.permission.type);
10435
- const pattern = entry.toolName === "launch-process" && entry.shellInputRegex ? shellRegexToGlob(entry.shellInputRegex) : "*";
10486
+ let pattern;
10487
+ if (entry.toolName === "launch-process" && entry.shellInputRegex) {
10488
+ const regex = entry.shellInputRegex;
10489
+ if (isShellRegexRoundtrippable(regex)) {
10490
+ pattern = shellRegexToGlob(regex);
10491
+ } else {
10492
+ if (action === "deny") {
10493
+ logger5?.warn(
10494
+ `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.`
10495
+ );
10496
+ pattern = "*";
10497
+ } else {
10498
+ pattern = shellRegexToGlob(regex);
10499
+ logger5?.warn(
10500
+ `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.`
10501
+ );
10502
+ }
10503
+ }
10504
+ } else {
10505
+ pattern = "*";
10506
+ }
10436
10507
  if (!permission[canonical]) {
10437
10508
  permission[canonical] = {};
10438
10509
  }
@@ -10687,6 +10758,12 @@ var ClinePermissions = class _ClinePermissions extends ToolPermissions {
10687
10758
  ...params,
10688
10759
  fileContent: params.fileContent ?? "{}"
10689
10760
  });
10761
+ if (params.validate) {
10762
+ const result = this.validate();
10763
+ if (!result.success) {
10764
+ throw result.error;
10765
+ }
10766
+ }
10690
10767
  }
10691
10768
  isDeletable() {
10692
10769
  return false;
@@ -10825,7 +10902,19 @@ var ClinePermissions = class _ClinePermissions extends ToolPermissions {
10825
10902
  });
10826
10903
  }
10827
10904
  validate() {
10828
- return { success: true, error: null };
10905
+ try {
10906
+ const parsed = JSON.parse(this.fileContent || "{}");
10907
+ const result = ClineCommandPermissionsSchema.safeParse(parsed);
10908
+ if (!result.success) {
10909
+ return { success: false, error: result.error };
10910
+ }
10911
+ return { success: true, error: null };
10912
+ } catch (error) {
10913
+ return {
10914
+ success: false,
10915
+ error: new Error(`Failed to parse Cline permissions JSON: ${formatError(error)}`)
10916
+ };
10917
+ }
10829
10918
  }
10830
10919
  static forDeletion({
10831
10920
  outputRoot = process.cwd(),
@@ -10847,6 +10936,8 @@ var import_node_path71 = require("path");
10847
10936
  var smolToml4 = __toESM(require("smol-toml"), 1);
10848
10937
  var RULESYNC_PROFILE_NAME = "rulesync";
10849
10938
  var RULESYNC_BASH_RULES_FILE_NAME = "rulesync.rules";
10939
+ var CODEX_PROJECT_ROOTS_KEY = ":project_roots";
10940
+ var CODEX_GLOB_SCAN_MAX_DEPTH = 8;
10850
10941
  var CodexcliPermissions = class _CodexcliPermissions extends ToolPermissions {
10851
10942
  static getSettablePaths(_options = {}) {
10852
10943
  return {
@@ -10957,17 +11048,28 @@ function convertRulesyncToCodexProfile({
10957
11048
  logger: logger5
10958
11049
  }) {
10959
11050
  const filesystem = {};
11051
+ const projectRootFilesystem = {};
10960
11052
  const domains = {};
10961
11053
  for (const [toolName, rules] of Object.entries(config.permission)) {
10962
11054
  if (toolName === "read") {
10963
11055
  for (const [pattern, action] of Object.entries(rules)) {
10964
- filesystem[pattern] = mapReadAction(action);
11056
+ addFilesystemRule({
11057
+ filesystem,
11058
+ projectRootFilesystem,
11059
+ pattern,
11060
+ access: mapReadAction(action)
11061
+ });
10965
11062
  }
10966
11063
  continue;
10967
11064
  }
10968
11065
  if (toolName === "edit" || toolName === "write") {
10969
11066
  for (const [pattern, action] of Object.entries(rules)) {
10970
- filesystem[pattern] = mapWriteAction(action);
11067
+ addFilesystemRule({
11068
+ filesystem,
11069
+ projectRootFilesystem,
11070
+ pattern,
11071
+ access: mapWriteAction(action)
11072
+ });
10971
11073
  }
10972
11074
  continue;
10973
11075
  }
@@ -10987,6 +11089,12 @@ function convertRulesyncToCodexProfile({
10987
11089
  `Codex CLI permissions support only read/edit/write/webfetch categories. Skipping: ${toolName}`
10988
11090
  );
10989
11091
  }
11092
+ if (Object.keys(projectRootFilesystem).length > 0) {
11093
+ if (Object.keys(projectRootFilesystem).some((pattern) => pattern.includes("**"))) {
11094
+ filesystem.glob_scan_max_depth = CODEX_GLOB_SCAN_MAX_DEPTH;
11095
+ }
11096
+ filesystem[CODEX_PROJECT_ROOTS_KEY] = projectRootFilesystem;
11097
+ }
10990
11098
  return {
10991
11099
  ...Object.keys(filesystem).length > 0 ? { filesystem } : {},
10992
11100
  ...Object.keys(domains).length > 0 ? { network: { domains } } : {}
@@ -10998,13 +11106,14 @@ function convertCodexProfileToRulesync(profile) {
10998
11106
  permission.read = {};
10999
11107
  permission.edit = {};
11000
11108
  for (const [pattern, access] of Object.entries(profile.filesystem)) {
11001
- if (access === "none") {
11002
- permission.read[pattern] = "deny";
11003
- permission.edit[pattern] = "deny";
11004
- } else if (access === "read") {
11005
- permission.read[pattern] = "allow";
11006
- } else {
11007
- permission.edit[pattern] = "allow";
11109
+ if (isCodexFilesystemAccess(access)) {
11110
+ addRulesyncFilesystemRule(permission, pattern, access);
11111
+ continue;
11112
+ }
11113
+ if (isCodexFilesystemRuleTable(access)) {
11114
+ for (const [nestedPattern, nestedAccess] of Object.entries(access)) {
11115
+ addRulesyncFilesystemRule(permission, nestedPattern, nestedAccess);
11116
+ }
11008
11117
  }
11009
11118
  }
11010
11119
  }
@@ -11027,6 +11136,35 @@ function toCodexProfile(value) {
11027
11136
  ...domains ? { network: { domains } } : {}
11028
11137
  };
11029
11138
  }
11139
+ function addFilesystemRule({
11140
+ filesystem,
11141
+ projectRootFilesystem,
11142
+ pattern,
11143
+ access
11144
+ }) {
11145
+ if (canBeCodexFilesystemRoot(pattern)) {
11146
+ filesystem[pattern] = access;
11147
+ return;
11148
+ }
11149
+ projectRootFilesystem[pattern] = access;
11150
+ }
11151
+ function canBeCodexFilesystemRoot(pattern) {
11152
+ return (0, import_node_path71.isAbsolute)(pattern) || /^[A-Za-z]:[\\/]/.test(pattern) || pattern.startsWith("~/") || pattern === "~" || pattern.startsWith(":");
11153
+ }
11154
+ function addRulesyncFilesystemRule(permission, pattern, access) {
11155
+ if (access === "none") {
11156
+ permission.read ??= {};
11157
+ permission.edit ??= {};
11158
+ permission.read[pattern] = "deny";
11159
+ permission.edit[pattern] = "deny";
11160
+ } else if (access === "read") {
11161
+ permission.read ??= {};
11162
+ permission.read[pattern] = "allow";
11163
+ } else {
11164
+ permission.edit ??= {};
11165
+ permission.edit[pattern] = "allow";
11166
+ }
11167
+ }
11030
11168
  function toMutableTable(value) {
11031
11169
  if (!value || typeof value !== "object" || Array.isArray(value)) {
11032
11170
  return {};
@@ -11037,8 +11175,33 @@ function toFilesystemRecord(value) {
11037
11175
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
11038
11176
  const result = {};
11039
11177
  for (const [key, raw] of Object.entries(value)) {
11040
- if (typeof raw !== "string") continue;
11041
- if (raw === "read" || raw === "write" || raw === "none") {
11178
+ if (isCodexFilesystemAccess(raw)) {
11179
+ result[key] = raw;
11180
+ continue;
11181
+ }
11182
+ if (key === "glob_scan_max_depth" && typeof raw === "number") {
11183
+ result[key] = raw;
11184
+ continue;
11185
+ }
11186
+ const nested = toCodexFilesystemRuleTable(raw);
11187
+ if (nested) {
11188
+ result[key] = nested;
11189
+ }
11190
+ }
11191
+ return Object.keys(result).length > 0 ? result : void 0;
11192
+ }
11193
+ function isCodexFilesystemAccess(value) {
11194
+ return value === "read" || value === "write" || value === "none";
11195
+ }
11196
+ function isCodexFilesystemRuleTable(value) {
11197
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
11198
+ return Object.values(value).every(isCodexFilesystemAccess);
11199
+ }
11200
+ function toCodexFilesystemRuleTable(value) {
11201
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
11202
+ const result = {};
11203
+ for (const [key, raw] of Object.entries(value)) {
11204
+ if (isCodexFilesystemAccess(raw)) {
11042
11205
  result[key] = raw;
11043
11206
  }
11044
11207
  }
@@ -11418,7 +11581,7 @@ var RESERVED_OBJECT_KEYS = /* @__PURE__ */ new Set([
11418
11581
  "constructor",
11419
11582
  "prototype"
11420
11583
  ]);
11421
- var moduleLogger = new ConsoleLogger();
11584
+ var moduleLogger2 = new ConsoleLogger();
11422
11585
  var GeminicliPermissions = class _GeminicliPermissions extends ToolPermissions {
11423
11586
  static getSettablePaths(_options = {}) {
11424
11587
  return {
@@ -11447,7 +11610,7 @@ var GeminicliPermissions = class _GeminicliPermissions extends ToolPermissions {
11447
11610
  rulesyncPermissions,
11448
11611
  validate = true,
11449
11612
  global = false,
11450
- logger: logger5 = moduleLogger
11613
+ logger: logger5 = moduleLogger2
11451
11614
  }) {
11452
11615
  const paths = this.getSettablePaths({ global });
11453
11616
  const fileContent = buildGeminicliPolicyContent(rulesyncPermissions.getJson(), logger5);
@@ -11472,31 +11635,31 @@ var GeminicliPermissions = class _GeminicliPermissions extends ToolPermissions {
11472
11635
  { cause: error }
11473
11636
  );
11474
11637
  }
11475
- const rules = extractRules(parsed, moduleLogger);
11638
+ const rules = extractRules(parsed, moduleLogger2);
11476
11639
  for (const [index, rule] of rules.entries()) {
11477
11640
  const mappedCategory = Object.hasOwn(GEMINICLI_TO_RULESYNC_TOOL_NAME, rule.toolName) ? GEMINICLI_TO_RULESYNC_TOOL_NAME[rule.toolName] : void 0;
11478
11641
  const category = mappedCategory ?? rule.toolName;
11479
11642
  if (RESERVED_OBJECT_KEYS.has(category)) {
11480
- moduleLogger.warn(
11643
+ moduleLogger2.warn(
11481
11644
  `Skipping rule #${index} in ${this.getRelativeFilePath()}: toolName "${rule.toolName}" maps to a reserved object key ("${category}") and would risk prototype pollution.`
11482
11645
  );
11483
11646
  continue;
11484
11647
  }
11485
11648
  const action = mapFromGeminicliDecision(rule.decision);
11486
11649
  if (!action) {
11487
- moduleLogger.warn(
11650
+ moduleLogger2.warn(
11488
11651
  `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)}`
11489
11652
  );
11490
11653
  continue;
11491
11654
  }
11492
11655
  if (rule.toolName === "run_shell_command" && rule.commandPrefix !== void 0 && rule.argsPattern !== void 0) {
11493
- moduleLogger.warn(
11656
+ moduleLogger2.warn(
11494
11657
  `Rule #${index} in ${this.getRelativeFilePath()} sets both commandPrefix and argsPattern; rulesync will honor argsPattern and ignore commandPrefix=${JSON.stringify(rule.commandPrefix)}.`
11495
11658
  );
11496
11659
  }
11497
11660
  const pattern = extractPattern(rule);
11498
11661
  if (RESERVED_OBJECT_KEYS.has(pattern)) {
11499
- moduleLogger.warn(
11662
+ moduleLogger2.warn(
11500
11663
  `Skipping rule #${index} in ${this.getRelativeFilePath()}: pattern "${pattern}" is a reserved object key.`
11501
11664
  );
11502
11665
  continue;
@@ -12265,7 +12428,7 @@ var QwenSettingsPermissionsSchema = import_mini36.z.looseObject({
12265
12428
  var QwenSettingsSchema = import_mini36.z.looseObject({
12266
12429
  permissions: import_mini36.z.optional(QwenSettingsPermissionsSchema)
12267
12430
  });
12268
- var moduleLogger2 = new ConsoleLogger();
12431
+ var moduleLogger3 = new ConsoleLogger();
12269
12432
  var CANONICAL_TO_QWEN_TOOL_NAMES = {
12270
12433
  bash: "Bash",
12271
12434
  read: "Read",
@@ -12320,6 +12483,12 @@ var QwencodePermissions = class _QwencodePermissions extends ToolPermissions {
12320
12483
  ...params,
12321
12484
  fileContent: params.fileContent ?? "{}"
12322
12485
  });
12486
+ if (params.validate) {
12487
+ const result = this.validate();
12488
+ if (!result.success) {
12489
+ throw result.error;
12490
+ }
12491
+ }
12323
12492
  }
12324
12493
  isDeletable() {
12325
12494
  return false;
@@ -12441,7 +12610,19 @@ var QwencodePermissions = class _QwencodePermissions extends ToolPermissions {
12441
12610
  });
12442
12611
  }
12443
12612
  validate() {
12444
- return { success: true, error: null };
12613
+ try {
12614
+ const parsed = JSON.parse(this.fileContent || "{}");
12615
+ const result = QwenSettingsSchema.safeParse(parsed);
12616
+ if (!result.success) {
12617
+ return { success: false, error: result.error };
12618
+ }
12619
+ return { success: true, error: null };
12620
+ } catch (error) {
12621
+ return {
12622
+ success: false,
12623
+ error: new Error(`Failed to parse Qwen permissions JSON: ${formatError(error)}`)
12624
+ };
12625
+ }
12445
12626
  }
12446
12627
  static forDeletion({
12447
12628
  outputRoot = process.cwd(),
@@ -12482,7 +12663,7 @@ function convertRulesyncToQwenPermissions(config) {
12482
12663
  }
12483
12664
  function convertQwenToRulesyncPermissions(params) {
12484
12665
  const permission = {};
12485
- const logger5 = params.logger ?? moduleLogger2;
12666
+ const logger5 = params.logger ?? moduleLogger3;
12486
12667
  const processEntries = (entries, action) => {
12487
12668
  for (const entry of entries) {
12488
12669
  const parsed = parseQwenPermissionEntry(entry, { logger: logger5 });
@@ -13234,7 +13415,8 @@ var RulesyncSkillFrontmatterSchemaInternal = import_mini39.z.looseObject({
13234
13415
  "allowed-tools": import_mini39.z.optional(import_mini39.z.array(import_mini39.z.string())),
13235
13416
  model: import_mini39.z.optional(import_mini39.z.string()),
13236
13417
  "disable-model-invocation": import_mini39.z.optional(import_mini39.z.boolean()),
13237
- "scheduled-task": import_mini39.z.optional(import_mini39.z.boolean())
13418
+ "scheduled-task": import_mini39.z.optional(import_mini39.z.boolean()),
13419
+ paths: import_mini39.z.optional(import_mini39.z.union([import_mini39.z.string(), import_mini39.z.array(import_mini39.z.string())]))
13238
13420
  })
13239
13421
  ),
13240
13422
  codexcli: import_mini39.z.optional(
@@ -14009,7 +14191,8 @@ var ClaudecodeSkillFrontmatterSchema = import_mini43.z.looseObject({
14009
14191
  description: import_mini43.z.string(),
14010
14192
  "allowed-tools": import_mini43.z.optional(import_mini43.z.array(import_mini43.z.string())),
14011
14193
  model: import_mini43.z.optional(import_mini43.z.string()),
14012
- "disable-model-invocation": import_mini43.z.optional(import_mini43.z.boolean())
14194
+ "disable-model-invocation": import_mini43.z.optional(import_mini43.z.boolean()),
14195
+ paths: import_mini43.z.optional(import_mini43.z.union([import_mini43.z.string(), import_mini43.z.array(import_mini43.z.string())]))
14013
14196
  });
14014
14197
  var ClaudecodeSkill = class _ClaudecodeSkill extends ToolSkill {
14015
14198
  constructor({
@@ -14082,6 +14265,7 @@ var ClaudecodeSkill = class _ClaudecodeSkill extends ToolSkill {
14082
14265
  ...frontmatter["disable-model-invocation"] !== void 0 && {
14083
14266
  "disable-model-invocation": frontmatter["disable-model-invocation"]
14084
14267
  },
14268
+ ...frontmatter.paths !== void 0 && { paths: frontmatter.paths },
14085
14269
  ...this.relativeDirPath === CLAUDE_SCHEDULED_TASKS_DIR_PATH && { "scheduled-task": true }
14086
14270
  };
14087
14271
  const rulesyncFrontmatter = {
@@ -14119,6 +14303,9 @@ var ClaudecodeSkill = class _ClaudecodeSkill extends ToolSkill {
14119
14303
  },
14120
14304
  ...rulesyncFrontmatter.claudecode?.["disable-model-invocation"] !== void 0 && {
14121
14305
  "disable-model-invocation": rulesyncFrontmatter.claudecode["disable-model-invocation"]
14306
+ },
14307
+ ...rulesyncFrontmatter.claudecode?.paths !== void 0 && {
14308
+ paths: rulesyncFrontmatter.claudecode.paths
14122
14309
  }
14123
14310
  };
14124
14311
  const settablePaths = _ClaudecodeSkill.getSettablePaths({ global });
@@ -21063,6 +21250,8 @@ var CopilotRuleFrontmatterSchema = import_mini77.z.object({
21063
21250
  applyTo: import_mini77.z.optional(import_mini77.z.string()),
21064
21251
  excludeAgent: import_mini77.z.optional(import_mini77.z.union([import_mini77.z.literal("code-review"), import_mini77.z.literal("coding-agent")]))
21065
21252
  });
21253
+ var normalizeRelativePath = (path5) => path5.replace(/\\/g, "/").replace(/\/+/g, "/");
21254
+ var sameRelativePath = (leftDir, leftFile, rightDir, rightFile) => normalizeRelativePath((0, import_node_path136.join)(leftDir, leftFile)) === normalizeRelativePath((0, import_node_path136.join)(rightDir, rightFile));
21066
21255
  var CopilotRule = class _CopilotRule extends ToolRule {
21067
21256
  frontmatter;
21068
21257
  body;
@@ -21180,7 +21369,12 @@ var CopilotRule = class _CopilotRule extends ToolRule {
21180
21369
  global = false
21181
21370
  }) {
21182
21371
  const paths = this.getSettablePaths({ global });
21183
- const isRoot = relativeDirPath ? (0, import_node_path136.join)(relativeDirPath, relativeFilePath) === (0, import_node_path136.join)(paths.root.relativeDirPath, paths.root.relativeFilePath) : relativeFilePath === paths.root.relativeFilePath;
21372
+ const isRoot = relativeDirPath ? sameRelativePath(
21373
+ relativeDirPath,
21374
+ relativeFilePath,
21375
+ paths.root.relativeDirPath,
21376
+ paths.root.relativeFilePath
21377
+ ) : relativeFilePath === paths.root.relativeFilePath;
21184
21378
  const resolvedRelativeDirPath = relativeDirPath ?? (isRoot ? paths.root.relativeDirPath : paths.nonRoot?.relativeDirPath ?? paths.root.relativeDirPath);
21185
21379
  if (isRoot) {
21186
21380
  const relativePath2 = (0, import_node_path136.join)(paths.root.relativeDirPath, paths.root.relativeFilePath);
@@ -21224,7 +21418,12 @@ var CopilotRule = class _CopilotRule extends ToolRule {
21224
21418
  global = false
21225
21419
  }) {
21226
21420
  const paths = this.getSettablePaths({ global });
21227
- const isRoot = (0, import_node_path136.join)(relativeDirPath, relativeFilePath) === (0, import_node_path136.join)(paths.root.relativeDirPath, paths.root.relativeFilePath);
21421
+ const isRoot = sameRelativePath(
21422
+ relativeDirPath,
21423
+ relativeFilePath,
21424
+ paths.root.relativeDirPath,
21425
+ paths.root.relativeFilePath
21426
+ );
21228
21427
  return new _CopilotRule({
21229
21428
  outputRoot,
21230
21429
  relativeDirPath,
@@ -26171,7 +26370,10 @@ var GITIGNORE_ENTRY_REGISTRY = [
26171
26370
  { target: "kilo", feature: "commands", entry: "**/.kilo/workflows/" },
26172
26371
  { target: "kilo", feature: "mcp", entry: "**/.kilo/mcp.json" },
26173
26372
  { target: "kilo", feature: "ignore", entry: "**/.kiloignore" },
26174
- { target: "kilo", feature: "permissions", entry: "**/kilo.jsonc" },
26373
+ // No `**/kilo.jsonc` entry: structurally identical to `opencode.jsonc` (no
26374
+ // entry). The Kilo translator preserves non-permissions Kilo settings on
26375
+ // round-trip, so the file is intended to be checked in by the user — adding
26376
+ // `**/kilo.jsonc` would be too aggressive.
26175
26377
  // Kiro
26176
26378
  { target: "kiro", feature: "rules", entry: "**/.kiro/steering/" },
26177
26379
  { target: "kiro", feature: "commands", entry: "**/.kiro/prompts/" },
@@ -31509,7 +31711,7 @@ function wrapCommand({
31509
31711
  }
31510
31712
 
31511
31713
  // src/cli/index.ts
31512
- var getVersion = () => "8.15.0";
31714
+ var getVersion = () => "8.16.0";
31513
31715
  function wrapCommand2(name, errorCode, handler) {
31514
31716
  return wrapCommand({ name, errorCode, handler, getVersion });
31515
31717
  }
package/dist/cli/index.js CHANGED
@@ -79,7 +79,7 @@ import {
79
79
  stringifyFrontmatter,
80
80
  toPosixPath,
81
81
  writeFileContent
82
- } from "../chunk-UMJWBQ2X.js";
82
+ } from "../chunk-DOWXY74I.js";
83
83
 
84
84
  // src/cli/index.ts
85
85
  import { Command } from "commander";
@@ -1408,7 +1408,10 @@ var GITIGNORE_ENTRY_REGISTRY = [
1408
1408
  { target: "kilo", feature: "commands", entry: "**/.kilo/workflows/" },
1409
1409
  { target: "kilo", feature: "mcp", entry: "**/.kilo/mcp.json" },
1410
1410
  { target: "kilo", feature: "ignore", entry: "**/.kiloignore" },
1411
- { target: "kilo", feature: "permissions", entry: "**/kilo.jsonc" },
1411
+ // No `**/kilo.jsonc` entry: structurally identical to `opencode.jsonc` (no
1412
+ // entry). The Kilo translator preserves non-permissions Kilo settings on
1413
+ // round-trip, so the file is intended to be checked in by the user — adding
1414
+ // `**/kilo.jsonc` would be too aggressive.
1412
1415
  // Kiro
1413
1416
  { target: "kiro", feature: "rules", entry: "**/.kiro/steering/" },
1414
1417
  { target: "kiro", feature: "commands", entry: "**/.kiro/prompts/" },
@@ -6482,7 +6485,7 @@ function wrapCommand({
6482
6485
  }
6483
6486
 
6484
6487
  // src/cli/index.ts
6485
- var getVersion = () => "8.15.0";
6488
+ var getVersion = () => "8.16.0";
6486
6489
  function wrapCommand2(name, errorCode, handler) {
6487
6490
  return wrapCommand({ name, errorCode, handler, getVersion });
6488
6491
  }