rulesync 7.24.0 → 7.26.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.
package/README.md CHANGED
@@ -71,7 +71,7 @@ See [Quick Start guide](https://dyoshikawa.github.io/rulesync/getting-started/qu
71
71
  | Gemini CLI | geminicli | ✅ 🌏 | ✅ | ✅ 🌏 | ✅ 🌏 | 🎮 | ✅ 🌏 | ✅ 🌏 |
72
72
  | Goose | goose | ✅ 🌏 | ✅ | | | | | |
73
73
  | GitHub Copilot | copilot | ✅ 🌏 | | ✅ | ✅ | ✅ | ✅ | ✅ |
74
- | GitHub Copilot CLI | copilotcli | | | ✅ 🌏 | | | | |
74
+ | GitHub Copilot CLI | copilotcli | ✅ 🌏 | | ✅ 🌏 | | | | |
75
75
  | Cursor | cursor | ✅ | ✅ | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ |
76
76
  | deepagents-cli | deepagents | ✅ | | ✅ 🌏 | | ✅ | ✅ | 🌏 |
77
77
  | Factory Droid | factorydroid | ✅ 🌏 | | ✅ 🌏 | 🎮 | 🎮 | 🎮 | ✅ 🌏 |
@@ -137,6 +137,9 @@ async function readOrInitializeFileContent(filePath, initialContent = "") {
137
137
  return initialContent;
138
138
  }
139
139
  }
140
+ function toPosixPath(p) {
141
+ return p.replace(/\\/g, "/");
142
+ }
140
143
  function checkPathTraversal({
141
144
  relativePath,
142
145
  intendedRootDir
@@ -944,8 +947,11 @@ var AiFile = class {
944
947
  getFileContent() {
945
948
  return this.fileContent;
946
949
  }
950
+ /**
951
+ * Returns the relative path from CWD with POSIX separators for consistent cross-platform output.
952
+ */
947
953
  getRelativePathFromCwd() {
948
- return path.join(this.relativeDirPath, this.relativeFilePath).replace(/\\/g, "/");
954
+ return toPosixPath(path.join(this.relativeDirPath, this.relativeFilePath));
949
955
  }
950
956
  setFileContent(newFileContent) {
951
957
  this.fileContent = newFileContent;
@@ -6122,7 +6128,7 @@ import { z as z21 } from "zod/mini";
6122
6128
  // src/types/mcp.ts
6123
6129
  import { z as z20 } from "zod/mini";
6124
6130
  var McpServerSchema = z20.looseObject({
6125
- type: z20.optional(z20.enum(["stdio", "sse", "http"])),
6131
+ type: z20.optional(z20.enum(["local", "stdio", "sse", "http"])),
6126
6132
  command: z20.optional(z20.union([z20.string(), z20.array(z20.string())])),
6127
6133
  args: z20.optional(z20.array(z20.string())),
6128
6134
  url: z20.optional(z20.string()),
@@ -6133,7 +6139,7 @@ var McpServerSchema = z20.looseObject({
6133
6139
  timeout: z20.optional(z20.number()),
6134
6140
  trust: z20.optional(z20.boolean()),
6135
6141
  cwd: z20.optional(z20.string()),
6136
- transport: z20.optional(z20.enum(["stdio", "sse", "http"])),
6142
+ transport: z20.optional(z20.enum(["local", "stdio", "sse", "http"])),
6137
6143
  alwaysAllow: z20.optional(z20.array(z20.string())),
6138
6144
  tools: z20.optional(z20.array(z20.string())),
6139
6145
  kiroAutoApprove: z20.optional(z20.array(z20.string())),
@@ -6718,13 +6724,38 @@ var CopilotMcp = class _CopilotMcp extends ToolMcp {
6718
6724
 
6719
6725
  // src/features/mcp/copilotcli-mcp.ts
6720
6726
  import { join as join48 } from "path";
6727
+ var isRemoteServerType = (type) => {
6728
+ return type === "http" || type === "sse";
6729
+ };
6730
+ var resolveCopilotcliServerType = (server) => {
6731
+ if (server.type) {
6732
+ return server.type;
6733
+ }
6734
+ if (server.transport === "http" || server.transport === "sse" || server.transport === "local") {
6735
+ return server.transport;
6736
+ }
6737
+ return "stdio";
6738
+ };
6721
6739
  function addTypeField(mcpServers) {
6722
6740
  const result = {};
6723
6741
  for (const [name, server] of Object.entries(mcpServers)) {
6724
6742
  const parsed = McpServerSchema.parse(server);
6743
+ const type = resolveCopilotcliServerType(parsed);
6744
+ if (isRemoteServerType(type)) {
6745
+ if (!parsed.url && !parsed.httpUrl) {
6746
+ throw new Error(
6747
+ `MCP server "${name}" is missing a url or httpUrl. GitHub Copilot CLI ${type} servers require a non-empty url or httpUrl.`
6748
+ );
6749
+ }
6750
+ result[name] = {
6751
+ ...parsed,
6752
+ type
6753
+ };
6754
+ continue;
6755
+ }
6725
6756
  if (!parsed.command) {
6726
6757
  throw new Error(
6727
- `MCP server "${name}" is missing a command. GitHub Copilot CLI stdio servers require a non-empty command.`
6758
+ `MCP server "${name}" is missing a command. GitHub Copilot CLI ${type} servers require a non-empty command.`
6728
6759
  );
6729
6760
  }
6730
6761
  let command;
@@ -6741,10 +6772,10 @@ function addTypeField(mcpServers) {
6741
6772
  args = cmdArgs.length > 0 ? [...cmdArgs, ...parsed.args ?? []] : parsed.args;
6742
6773
  }
6743
6774
  result[name] = {
6744
- type: "stdio",
6775
+ ...parsed,
6776
+ type,
6745
6777
  command,
6746
- ...args && { args },
6747
- ...parsed.env && { env: parsed.env }
6778
+ ...args && { args }
6748
6779
  };
6749
6780
  }
6750
6781
  return result;
@@ -6752,6 +6783,10 @@ function addTypeField(mcpServers) {
6752
6783
  function removeTypeField(config) {
6753
6784
  const result = {};
6754
6785
  for (const [name, server] of Object.entries(config.mcpServers ?? {})) {
6786
+ if (server.type !== "stdio") {
6787
+ result[name] = server;
6788
+ continue;
6789
+ }
6755
6790
  const { type: _, ...rest } = server;
6756
6791
  result[name] = rest;
6757
6792
  }
@@ -6774,13 +6809,7 @@ var CopilotcliMcp = class _CopilotcliMcp extends ToolMcp {
6774
6809
  isDeletable() {
6775
6810
  return !this.global;
6776
6811
  }
6777
- static getSettablePaths({ global } = {}) {
6778
- if (global) {
6779
- return {
6780
- relativeDirPath: ".copilot",
6781
- relativeFilePath: "mcp-config.json"
6782
- };
6783
- }
6812
+ static getSettablePaths(_options = {}) {
6784
6813
  return {
6785
6814
  relativeDirPath: ".copilot",
6786
6815
  relativeFilePath: "mcp-config.json"
@@ -8396,8 +8425,11 @@ var AiDir = class {
8396
8425
  getOtherFiles() {
8397
8426
  return this.otherFiles;
8398
8427
  }
8428
+ /**
8429
+ * Returns the relative path from CWD with POSIX separators for consistent cross-platform output.
8430
+ */
8399
8431
  getRelativePathFromCwd() {
8400
- return path2.join(this.relativeDirPath, this.dirName).replace(/\\/g, "/");
8432
+ return toPosixPath(path2.join(this.relativeDirPath, this.dirName));
8401
8433
  }
8402
8434
  getGlobal() {
8403
8435
  return this.global;
@@ -15755,6 +15787,42 @@ var CopilotRule = class _CopilotRule extends ToolRule {
15755
15787
  }
15756
15788
  };
15757
15789
 
15790
+ // src/features/rules/copilotcli-rule.ts
15791
+ var CopilotcliRule = class _CopilotcliRule extends CopilotRule {
15792
+ static fromCopilotRule(copilotRule, validate = true) {
15793
+ return new _CopilotcliRule({
15794
+ baseDir: copilotRule.getBaseDir(),
15795
+ relativeDirPath: copilotRule.getRelativeDirPath(),
15796
+ relativeFilePath: copilotRule.getRelativeFilePath(),
15797
+ frontmatter: copilotRule.getFrontmatter(),
15798
+ body: copilotRule.getBody(),
15799
+ validate,
15800
+ root: copilotRule.isRoot()
15801
+ });
15802
+ }
15803
+ static fromRulesyncRule({
15804
+ validate = true,
15805
+ ...rest
15806
+ }) {
15807
+ return this.fromCopilotRule(CopilotRule.fromRulesyncRule({ validate, ...rest }), validate);
15808
+ }
15809
+ static async fromFile({
15810
+ validate = true,
15811
+ ...rest
15812
+ }) {
15813
+ return this.fromCopilotRule(await CopilotRule.fromFile({ validate, ...rest }), validate);
15814
+ }
15815
+ static forDeletion(params) {
15816
+ return this.fromCopilotRule(CopilotRule.forDeletion(params), false);
15817
+ }
15818
+ static isTargetedByRulesyncRule(rulesyncRule) {
15819
+ return this.isTargetedByRulesyncRuleDefault({
15820
+ rulesyncRule,
15821
+ toolTarget: "copilotcli"
15822
+ });
15823
+ }
15824
+ };
15825
+
15758
15826
  // src/features/rules/cursor-rule.ts
15759
15827
  import { join as join111 } from "path";
15760
15828
  import { z as z60 } from "zod/mini";
@@ -16940,7 +17008,7 @@ var RooRule = class _RooRule extends ToolRule {
16940
17008
 
16941
17009
  // src/features/rules/rovodev-rule.ts
16942
17010
  import { join as join123 } from "path";
16943
- var DISALLOWED_ROVODEV_MODULAR_RULE_BASENAMES = /* @__PURE__ */ new Set(["AGENTS.md", "AGENTS.local.md"]);
17011
+ var DISALLOWED_ROVODEV_MODULAR_RULE_BASENAMES = /* @__PURE__ */ new Set(["agents.md", "agents.local.md"]);
16944
17012
  var RovodevRule = class _RovodevRule extends ToolRule {
16945
17013
  /**
16946
17014
  * Whether `relativePath` (posix-style path relative to modular-rules root) may be imported as a modular rule.
@@ -16954,7 +17022,7 @@ var RovodevRule = class _RovodevRule extends ToolRule {
16954
17022
  if (segment === "" || segment === "." || segment === "..") {
16955
17023
  continue;
16956
17024
  }
16957
- if (DISALLOWED_ROVODEV_MODULAR_RULE_BASENAMES.has(segment)) {
17025
+ if (DISALLOWED_ROVODEV_MODULAR_RULE_BASENAMES.has(segment.toLowerCase())) {
16958
17026
  return false;
16959
17027
  }
16960
17028
  }
@@ -16996,22 +17064,54 @@ var RovodevRule = class _RovodevRule extends ToolRule {
16996
17064
  }) {
16997
17065
  const paths = this.getSettablePaths({ global });
16998
17066
  if (!global && "nonRoot" in paths && paths.nonRoot && overrideDirPath === paths.nonRoot.relativeDirPath) {
16999
- if (!this.isAllowedModularRulesRelativePath(relativeFilePath)) {
17000
- throw new Error(
17001
- `Reserved Rovodev memory basename under modular-rules (not a modular rule): ${join123(overrideDirPath, relativeFilePath)}`
17002
- );
17003
- }
17004
- const fileContent2 = await readFileContent(join123(baseDir, overrideDirPath, relativeFilePath));
17005
- return new _RovodevRule({
17067
+ return this.fromModularFile({
17006
17068
  baseDir,
17007
- relativeDirPath: overrideDirPath,
17008
17069
  relativeFilePath,
17009
- fileContent: fileContent2,
17070
+ relativeDirPath: overrideDirPath,
17010
17071
  validate,
17011
- global,
17012
- root: false
17072
+ global
17013
17073
  });
17014
17074
  }
17075
+ return this.fromRootFile({
17076
+ baseDir,
17077
+ relativeFilePath,
17078
+ overrideDirPath,
17079
+ validate,
17080
+ global,
17081
+ paths
17082
+ });
17083
+ }
17084
+ static async fromModularFile({
17085
+ baseDir,
17086
+ relativeFilePath,
17087
+ relativeDirPath,
17088
+ validate,
17089
+ global
17090
+ }) {
17091
+ if (!this.isAllowedModularRulesRelativePath(relativeFilePath)) {
17092
+ throw new Error(
17093
+ `Reserved Rovodev memory basename under modular-rules (not a modular rule): ${join123(relativeDirPath, relativeFilePath)}`
17094
+ );
17095
+ }
17096
+ const fileContent = await readFileContent(join123(baseDir, relativeDirPath, relativeFilePath));
17097
+ return new _RovodevRule({
17098
+ baseDir,
17099
+ relativeDirPath,
17100
+ relativeFilePath,
17101
+ fileContent,
17102
+ validate,
17103
+ global,
17104
+ root: false
17105
+ });
17106
+ }
17107
+ static async fromRootFile({
17108
+ baseDir,
17109
+ relativeFilePath,
17110
+ overrideDirPath,
17111
+ validate,
17112
+ global,
17113
+ paths
17114
+ }) {
17015
17115
  const relativeDirPath = overrideDirPath ?? paths.root.relativeDirPath;
17016
17116
  const agentsMdExpectedLocationsDescription = "alternativeRoots" in paths && paths.alternativeRoots && paths.alternativeRoots.length > 0 ? `${join123(paths.root.relativeDirPath, paths.root.relativeFilePath)} or project root` : join123(paths.root.relativeDirPath, paths.root.relativeFilePath);
17017
17117
  if (relativeFilePath !== "AGENTS.md") {
@@ -17300,6 +17400,7 @@ var rulesProcessorToolTargets = [
17300
17400
  "cline",
17301
17401
  "codexcli",
17302
17402
  "copilot",
17403
+ "copilotcli",
17303
17404
  "cursor",
17304
17405
  "deepagents",
17305
17406
  "factorydroid",
@@ -17423,6 +17524,17 @@ var toolRuleFactories = /* @__PURE__ */ new Map([
17423
17524
  }
17424
17525
  }
17425
17526
  ],
17527
+ [
17528
+ "copilotcli",
17529
+ {
17530
+ class: CopilotcliRule,
17531
+ meta: {
17532
+ extension: "md",
17533
+ supportsGlobal: true,
17534
+ ruleDiscoveryMode: "auto"
17535
+ }
17536
+ }
17537
+ ],
17426
17538
  [
17427
17539
  "cursor",
17428
17540
  {
@@ -17942,6 +18054,24 @@ var RulesProcessor = class extends FeatureProcessor {
17942
18054
  const dirName = dirname3(relative5(this.baseDir, filePath));
17943
18055
  return dirName === "" ? "." : dirName;
17944
18056
  };
18057
+ const buildDeletionRulesFromPaths = (filePaths, opts) => {
18058
+ const isNonRoot = opts !== void 0;
18059
+ const effectiveBaseDir = isNonRoot ? opts.baseDirOverride : this.baseDir;
18060
+ return filePaths.map((filePath) => {
18061
+ const relativeDirPath = isNonRoot ? opts.relativeDirPathOverride : resolveRelativeDirPath(filePath);
18062
+ const relativeFilePath = isNonRoot ? relative5(effectiveBaseDir, filePath) : basename10(filePath);
18063
+ checkPathTraversal({
18064
+ relativePath: isNonRoot ? relativeFilePath : relativeDirPath,
18065
+ intendedRootDir: effectiveBaseDir
18066
+ });
18067
+ return factory.class.forDeletion({
18068
+ baseDir: this.baseDir,
18069
+ relativeDirPath,
18070
+ relativeFilePath,
18071
+ global: this.global
18072
+ });
18073
+ }).filter((rule) => rule.isDeletable());
18074
+ };
17945
18075
  const findFilesWithFallback = async (primaryGlob, alternativeRoots, buildAltGlob) => {
17946
18076
  const primaryFilePaths = await findFilesByGlobs(primaryGlob);
17947
18077
  if (primaryFilePaths.length > 0) {
@@ -17966,19 +18096,7 @@ var RulesProcessor = class extends FeatureProcessor {
17966
18096
  (alt) => join126(this.baseDir, alt.relativeDirPath, alt.relativeFilePath)
17967
18097
  );
17968
18098
  if (forDeletion) {
17969
- return uniqueRootFilePaths.map((filePath) => {
17970
- const relativeDirPath = resolveRelativeDirPath(filePath);
17971
- checkPathTraversal({
17972
- relativePath: relativeDirPath,
17973
- intendedRootDir: this.baseDir
17974
- });
17975
- return factory.class.forDeletion({
17976
- baseDir: this.baseDir,
17977
- relativeDirPath,
17978
- relativeFilePath: basename10(filePath),
17979
- global: this.global
17980
- });
17981
- }).filter((rule) => rule.isDeletable());
18099
+ return buildDeletionRulesFromPaths(uniqueRootFilePaths);
17982
18100
  }
17983
18101
  return await Promise.all(
17984
18102
  uniqueRootFilePaths.map((filePath) => {
@@ -18008,19 +18126,7 @@ var RulesProcessor = class extends FeatureProcessor {
18008
18126
  const uniqueLocalRootFilePaths2 = await findFilesByGlobs(
18009
18127
  join126(this.baseDir, "AGENTS.local.md")
18010
18128
  );
18011
- return uniqueLocalRootFilePaths2.map((filePath) => {
18012
- const relativeDirPath = resolveRelativeDirPath(filePath);
18013
- checkPathTraversal({
18014
- relativePath: relativeDirPath,
18015
- intendedRootDir: this.baseDir
18016
- });
18017
- return factory.class.forDeletion({
18018
- baseDir: this.baseDir,
18019
- relativeDirPath,
18020
- relativeFilePath: basename10(filePath),
18021
- global: this.global
18022
- });
18023
- }).filter((rule) => rule.isDeletable());
18129
+ return buildDeletionRulesFromPaths(uniqueLocalRootFilePaths2);
18024
18130
  }
18025
18131
  if (this.toolTarget !== "claudecode" && this.toolTarget !== "claudecode-legacy") {
18026
18132
  return [];
@@ -18033,19 +18139,7 @@ var RulesProcessor = class extends FeatureProcessor {
18033
18139
  settablePaths.alternativeRoots,
18034
18140
  (alt) => join126(this.baseDir, alt.relativeDirPath, "CLAUDE.local.md")
18035
18141
  );
18036
- return uniqueLocalRootFilePaths.map((filePath) => {
18037
- const relativeDirPath = resolveRelativeDirPath(filePath);
18038
- checkPathTraversal({
18039
- relativePath: relativeDirPath,
18040
- intendedRootDir: this.baseDir
18041
- });
18042
- return factory.class.forDeletion({
18043
- baseDir: this.baseDir,
18044
- relativeDirPath,
18045
- relativeFilePath: basename10(filePath),
18046
- global: this.global
18047
- });
18048
- }).filter((rule) => rule.isDeletable());
18142
+ return buildDeletionRulesFromPaths(uniqueLocalRootFilePaths);
18049
18143
  })();
18050
18144
  this.logger.debug(
18051
18145
  `Found ${localRootToolRules.length} local root tool rule files for deletion`
@@ -18059,19 +18153,7 @@ var RulesProcessor = class extends FeatureProcessor {
18059
18153
  return [];
18060
18154
  }
18061
18155
  const mirrorPaths = await findFilesByGlobs(join126(this.baseDir, "AGENTS.md"));
18062
- return mirrorPaths.map((filePath) => {
18063
- const relativeDirPath = resolveRelativeDirPath(filePath);
18064
- checkPathTraversal({
18065
- relativePath: relativeDirPath,
18066
- intendedRootDir: this.baseDir
18067
- });
18068
- return factory.class.forDeletion({
18069
- baseDir: this.baseDir,
18070
- relativeDirPath,
18071
- relativeFilePath: basename10(filePath),
18072
- global: this.global
18073
- });
18074
- }).filter((rule) => rule.isDeletable());
18156
+ return buildDeletionRulesFromPaths(mirrorPaths);
18075
18157
  })();
18076
18158
  const nonRootToolRules = await (async () => {
18077
18159
  if (!settablePaths.nonRoot) {
@@ -18081,8 +18163,14 @@ var RulesProcessor = class extends FeatureProcessor {
18081
18163
  const nonRootFilePaths = await findFilesByGlobs(
18082
18164
  join126(nonRootBaseDir, "**", `*.${factory.meta.extension}`)
18083
18165
  );
18166
+ if (forDeletion) {
18167
+ return buildDeletionRulesFromPaths(nonRootFilePaths, {
18168
+ baseDirOverride: nonRootBaseDir,
18169
+ relativeDirPathOverride: settablePaths.nonRoot.relativeDirPath
18170
+ });
18171
+ }
18084
18172
  const modularRootRelative = settablePaths.nonRoot.relativeDirPath;
18085
- const nonRootPathsForImport = !forDeletion && this.toolTarget === "rovodev" ? nonRootFilePaths.filter((filePath) => {
18173
+ const nonRootPathsForImport = this.toolTarget === "rovodev" ? nonRootFilePaths.filter((filePath) => {
18086
18174
  const relativeFilePath = relative5(nonRootBaseDir, filePath);
18087
18175
  const ok = RovodevRule.isAllowedModularRulesRelativePath(relativeFilePath);
18088
18176
  if (!ok) {
@@ -18092,21 +18180,6 @@ var RulesProcessor = class extends FeatureProcessor {
18092
18180
  }
18093
18181
  return ok;
18094
18182
  }) : nonRootFilePaths;
18095
- if (forDeletion) {
18096
- return nonRootFilePaths.map((filePath) => {
18097
- const relativeFilePath = relative5(nonRootBaseDir, filePath);
18098
- checkPathTraversal({
18099
- relativePath: relativeFilePath,
18100
- intendedRootDir: nonRootBaseDir
18101
- });
18102
- return factory.class.forDeletion({
18103
- baseDir: this.baseDir,
18104
- relativeDirPath: settablePaths.nonRoot?.relativeDirPath ?? ".",
18105
- relativeFilePath,
18106
- global: this.global
18107
- });
18108
- }).filter((rule) => rule.isDeletable());
18109
- }
18110
18183
  return await Promise.all(
18111
18184
  nonRootPathsForImport.map((filePath) => {
18112
18185
  const relativeFilePath = relative5(nonRootBaseDir, filePath);
@@ -19093,6 +19166,7 @@ export {
19093
19166
  FETCH_CONCURRENCY_LIMIT,
19094
19167
  formatError,
19095
19168
  ensureDir,
19169
+ toPosixPath,
19096
19170
  checkPathTraversal,
19097
19171
  directoryExists,
19098
19172
  readFileContent,