rulesync 9.6.1 → 9.6.3

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.
@@ -12644,6 +12644,13 @@ function mapOauthFromCodex(oauth) {
12644
12644
  }
12645
12645
  return result;
12646
12646
  }
12647
+ const CODEX_MCP_SERVER_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
12648
+ function normalizeCodexMcpServerName(name) {
12649
+ if (PROTOTYPE_POLLUTION_KEYS.has(name)) return null;
12650
+ if (CODEX_MCP_SERVER_NAME_PATTERN.test(name)) return name;
12651
+ const normalizedName = name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
12652
+ return normalizedName && !PROTOTYPE_POLLUTION_KEYS.has(normalizedName) ? normalizedName : null;
12653
+ }
12647
12654
  function convertFromCodexFormat(codexMcp) {
12648
12655
  const result = {};
12649
12656
  for (const [name, config] of Object.entries(codexMcp)) {
@@ -12666,8 +12673,13 @@ function convertFromCodexFormat(codexMcp) {
12666
12673
  }
12667
12674
  function convertToCodexFormat(mcpServers) {
12668
12675
  const result = {};
12676
+ const originalNames = /* @__PURE__ */ new Map();
12669
12677
  for (const [name, config] of Object.entries(mcpServers)) {
12670
- if (PROTOTYPE_POLLUTION_KEYS.has(name)) continue;
12678
+ const codexName = normalizeCodexMcpServerName(name);
12679
+ if (codexName === null) {
12680
+ warnWithFallback(void 0, `MCP server "${name}" could not be normalized to a valid Codex MCP server name and was skipped.`);
12681
+ continue;
12682
+ }
12671
12683
  if (!isRecord(config)) continue;
12672
12684
  const converted = {};
12673
12685
  for (const [key, value] of Object.entries(config)) {
@@ -12681,7 +12693,10 @@ function convertToCodexFormat(mcpServers) {
12681
12693
  else warnWithFallback(void 0, `[CodexCliMcp] Skipping invalid value type for mapped key '${key}': expected string array, got ${typeof value}`);
12682
12694
  } else converted[key] = value;
12683
12695
  }
12684
- result[name] = converted;
12696
+ const previousName = originalNames.get(codexName);
12697
+ if (previousName !== void 0) warnWithFallback(void 0, `Codex MCP server name collision: "${previousName}" and "${name}" both normalize to "${codexName}". Only the last processed server will be used.`);
12698
+ originalNames.set(codexName, name);
12699
+ result[codexName] = converted;
12685
12700
  }
12686
12701
  return result;
12687
12702
  }
@@ -36637,30 +36652,31 @@ var RovodevRule = class RovodevRule extends ToolRule {
36637
36652
  });
36638
36653
  }
36639
36654
  /**
36640
- * Mirror the primary `.rovodev/AGENTS.md` root rule to `./AGENTS.md` so project
36641
- * memory stays discoverable at the repo root. Empty when `rootRule` is not the primary root.
36655
+ * Root-mirror contract: rovodev mirrors its primary `.rovodev/AGENTS.md` root
36656
+ * rule to `./AGENTS.md` so project memory stays discoverable at the repo root.
36657
+ * Generation (`getMirrorFiles`) and deletion (`getMirrorDeletionGlobs`) are
36658
+ * bundled in one object so they cannot drift out of symmetry. The processor
36659
+ * keys off this method's mere presence to decide the tool mirrors at all.
36642
36660
  */
36643
- static getRootMirrorFiles({ outputRoot, rootRule, content }) {
36644
- if (!(rootRule instanceof RovodevRule)) return [];
36645
- const primary = this.getSettablePaths({ global: false }).root;
36646
- if (rootRule.getRelativeDirPath() !== primary.relativeDirPath || rootRule.getRelativeFilePath() !== primary.relativeFilePath) return [];
36647
- return [new RovodevRule({
36648
- outputRoot,
36649
- relativeDirPath: ".",
36650
- relativeFilePath: ROVODEV_RULE_FILE_NAME,
36651
- fileContent: content,
36652
- validate: true,
36653
- root: true
36654
- })];
36655
- }
36656
- /**
36657
- * Globs for mirror deletion: the `./AGENTS.md` mirror (`mirrorGlob`) is deleted
36658
- * only when the primary `.rovodev/AGENTS.md` (`primaryGlob`) still exists.
36659
- */
36660
- static getRootMirrorDeletionGlobs({ outputRoot }) {
36661
+ static getRootMirror() {
36661
36662
  return {
36662
- primaryGlob: (0, node_path.join)(outputRoot, ROVODEV_DIR, ROVODEV_RULE_FILE_NAME),
36663
- mirrorGlob: (0, node_path.join)(outputRoot, ROVODEV_RULE_FILE_NAME)
36663
+ getMirrorFiles: ({ outputRoot, rootRule, content }) => {
36664
+ if (!(rootRule instanceof RovodevRule)) return [];
36665
+ const primary = RovodevRule.getSettablePaths({ global: false }).root;
36666
+ if (rootRule.getRelativeDirPath() !== primary.relativeDirPath || rootRule.getRelativeFilePath() !== primary.relativeFilePath) return [];
36667
+ return [new RovodevRule({
36668
+ outputRoot,
36669
+ relativeDirPath: ".",
36670
+ relativeFilePath: ROVODEV_RULE_FILE_NAME,
36671
+ fileContent: content,
36672
+ validate: true,
36673
+ root: true
36674
+ })];
36675
+ },
36676
+ getMirrorDeletionGlobs: ({ outputRoot }) => ({
36677
+ primaryGlob: (0, node_path.join)(outputRoot, ROVODEV_DIR, ROVODEV_RULE_FILE_NAME),
36678
+ mirrorGlob: (0, node_path.join)(outputRoot, ROVODEV_RULE_FILE_NAME)
36679
+ })
36664
36680
  };
36665
36681
  }
36666
36682
  /** Glob for the `separate-local-file` deletion; rovodev writes it at project root, not under `.rovodev/`. */
@@ -37283,8 +37299,7 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
37283
37299
  skills: { skillClass: RovodevSkill }
37284
37300
  },
37285
37301
  localRootMode: "separate-local-file",
37286
- localRootFileName: "AGENTS.local.md",
37287
- mirrorsRootToAgentsMd: true
37302
+ localRootFileName: "AGENTS.local.md"
37288
37303
  }
37289
37304
  }],
37290
37305
  ["takt", {
@@ -37471,7 +37486,8 @@ var RulesProcessor = class extends FeatureProcessor {
37471
37486
  if (!rootRule) return;
37472
37487
  const newContent = this.generateReferenceSectionFromMeta(meta, toolRules) + (!meta.createsSeparateConventionsRule && meta.additionalConventions ? this.generateAdditionalConventionsSectionFromMeta(meta) : "") + rootRule.getFileContent();
37473
37488
  rootRule.setFileContent(newContent);
37474
- if (meta.mirrorsRootToAgentsMd && !this.global && factory.class.getRootMirrorFiles) toolRules.push(...factory.class.getRootMirrorFiles({
37489
+ const rootMirror = factory.class.getRootMirror?.();
37490
+ if (rootMirror && !this.global) toolRules.push(...rootMirror.getMirrorFiles({
37475
37491
  outputRoot: this.outputRoot,
37476
37492
  rootRule,
37477
37493
  content: newContent
@@ -37713,8 +37729,9 @@ As this project's AI coding tool, you must follow the additional conventions bel
37713
37729
  })();
37714
37730
  this.logger.debug(`Found ${localRootToolRules.length} local root tool rule files for deletion`);
37715
37731
  const rootMirrorDeletionRules = await (async () => {
37716
- if (!forDeletion || this.global || !factory.meta.mirrorsRootToAgentsMd || !factory.class.getRootMirrorDeletionGlobs) return [];
37717
- const { primaryGlob, mirrorGlob } = factory.class.getRootMirrorDeletionGlobs({ outputRoot: this.outputRoot });
37732
+ const rootMirror = factory.class.getRootMirror?.();
37733
+ if (!forDeletion || this.global || !rootMirror) return [];
37734
+ const { primaryGlob, mirrorGlob } = rootMirror.getMirrorDeletionGlobs({ outputRoot: this.outputRoot });
37718
37735
  if ((await findFilesByGlobs(primaryGlob)).length === 0) return [];
37719
37736
  const mirrorPaths = await findFilesByGlobs(mirrorGlob);
37720
37737
  return buildDeletionRulesFromPaths(mirrorPaths);
@@ -38152,13 +38169,13 @@ const sharedFileKey = (path) => {
38152
38169
  const file = path.relativeFilePath.replace(/\\/g, "/");
38153
38170
  return dir === "" || dir === "." ? file : `${dir}/${file}`;
38154
38171
  };
38155
- const settablePathsForScope = (cls, global) => {
38172
+ const settablePathsForScope = ({ cls, global }) => {
38156
38173
  const paths = [];
38157
38174
  let settable;
38158
38175
  try {
38159
38176
  settable = cls.getSettablePaths?.({ global });
38160
38177
  } catch {
38161
- return paths;
38178
+ settable = void 0;
38162
38179
  }
38163
38180
  if (settable?.relativeFilePath) paths.push({
38164
38181
  relativeDirPath: settable.relativeDirPath ?? ".",
@@ -38168,16 +38185,22 @@ const settablePathsForScope = (cls, global) => {
38168
38185
  paths.push(settable.root);
38169
38186
  for (const alt of settable.alternativeRoots ?? []) paths.push(alt);
38170
38187
  }
38171
- let extra;
38188
+ let extra = [];
38172
38189
  try {
38173
38190
  extra = cls.getExtraSharedWritePaths?.({ global }) ?? [];
38174
38191
  } catch {
38175
- return paths;
38192
+ extra = [];
38176
38193
  }
38177
38194
  for (const path of extra) if (path.relativeFilePath) paths.push(path);
38178
38195
  return paths;
38179
38196
  };
38180
- const collectFactoryPaths = (factory) => [...settablePathsForScope(factory.class, false), ...settablePathsForScope(factory.class, true)];
38197
+ const collectFactoryPaths = (factory) => [...settablePathsForScope({
38198
+ cls: factory.class,
38199
+ global: false
38200
+ }), ...settablePathsForScope({
38201
+ cls: factory.class,
38202
+ global: true
38203
+ })];
38181
38204
  /**
38182
38205
  * Derive, from the processor registry, every on-disk file that two or more
38183
38206
  * features read-modify-write. Source of truth the generation step graph's
@@ -38553,7 +38576,7 @@ function computeRootFileOwnership(params) {
38553
38576
  const paths = factory.class.getSettablePaths({ global: params.global });
38554
38577
  if ("root" in paths && paths.root) register(paths.root.relativeDirPath, paths.root.relativeFilePath, target);
38555
38578
  if ("alternativeRoots" in paths && paths.alternativeRoots) for (const alt of paths.alternativeRoots) register(alt.relativeDirPath, alt.relativeFilePath, target);
38556
- if (!params.global && factory.meta.mirrorsRootToAgentsMd) register(".", AGENTSMD_RULE_FILE_NAME, target);
38579
+ if (!params.global && factory.class.getRootMirror) register(".", AGENTSMD_RULE_FILE_NAME, target);
38557
38580
  }
38558
38581
  return ownerByPath;
38559
38582
  }
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_import = require("./import-DW-a1SJq.cjs");
2
+ const require_import = require("./import-DnpMhZOY.cjs");
3
3
  //#region src/index.ts
4
4
  async function generate(options = {}) {
5
5
  const { silent = true, verbose = false, ...rest } = options;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { I as ConsoleLogger, Ot as ALL_FEATURES, P as ConfigResolver, a as convertFromTool$1, n as checkRulesyncDirExists, nt as ALL_TOOL_TARGETS, r as generate$1, t as importFromTool$1 } from "./import-tTMN0WRi.js";
1
+ import { I as ConsoleLogger, Ot as ALL_FEATURES, P as ConfigResolver, a as convertFromTool$1, n as checkRulesyncDirExists, nt as ALL_TOOL_TARGETS, r as generate$1, t as importFromTool$1 } from "./import-A1yGuzwv.js";
2
2
  //#region src/index.ts
3
3
  async function generate(options = {}) {
4
4
  const { silent = true, verbose = false, ...rest } = options;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rulesync",
3
- "version": "9.6.1",
3
+ "version": "9.6.3",
4
4
  "description": "Unified AI rules management CLI tool that generates configuration files for various AI development tools",
5
5
  "keywords": [
6
6
  "ai",