rulesync 9.8.0 → 10.1.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/dist/cli/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { $ as removeTempDirectory, A as RulesyncCommand, B as checkPathTraversal, C as RulesyncHooks, Ct as RULESYNC_RULES_RELATIVE_DIR_PATH, D as CLAUDECODE_MEMORIES_DIR_NAME, Dt as formatError, E as CLAUDECODE_LOCAL_RULE_FILE_NAME, Et as RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, F as findControlCharacter, G as findFilesByGlobs, H as directoryExists, I as ConsoleLogger, J as isSymlink, K as getFileSize, L as JsonLogger, M as stringifyFrontmatter, N as loadYaml, O as CLAUDECODE_SETTINGS_LOCAL_FILE_NAME, Ot as ALL_FEATURES, P as ConfigResolver, Q as removeFile, R as CLIError, S as HooksProcessor, St as RULESYNC_RELATIVE_DIR_PATH, T as CLAUDECODE_DIR, Tt as RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH, U as ensureDir, V as createTempDirectory, W as fileExists, X as readFileContent, Y as listDirectoryFiles, Z as removeDirectory, _ as RulesyncPermissions, _t as RULESYNC_MCP_RELATIVE_FILE_PATH, a as convertFromTool, at as MAX_FILE_SIZE, b as IgnoreProcessor, bt as RULESYNC_PERMISSIONS_FILE_NAME, c as RulesyncRuleFrontmatterSchema, ct as RULESYNC_COMMANDS_RELATIVE_DIR_PATH, d as RulesyncSubagentFrontmatterSchema, dt as RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH, et as toPosixPath, f as SkillsProcessor, ft as RULESYNC_HOOKS_FILE_NAME, g as SKILL_FILE_NAME$1, gt as RULESYNC_MCP_FILE_NAME, h as RulesyncSkillFrontmatterSchema, ht as RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH, i as getProcessorRegistryEntry, it as ToolTargetSchema, j as RulesyncCommandFrontmatterSchema, k as CLAUDECODE_SKILLS_DIR_PATH, kt as ALL_FEATURES_WITH_WILDCARD, l as SubagentsProcessor, lt as RULESYNC_CONFIG_RELATIVE_FILE_PATH, m as RulesyncSkill, mt as RULESYNC_IGNORE_RELATIVE_FILE_PATH, n as checkRulesyncDirExists, nt as ALL_TOOL_TARGETS, o as RulesProcessor, ot as RULESYNC_AIIGNORE_FILE_NAME, p as getLocalSkillDirNames, pt as RULESYNC_HOOKS_RELATIVE_FILE_PATH, q as getHomeDirectory, r as generate, rt as ALL_TOOL_TARGETS_WITH_WILDCARD, s as RulesyncRule, st as RULESYNC_AIIGNORE_RELATIVE_FILE_PATH, t as importFromTool, tt as writeFileContent, u as RulesyncSubagent, ut as RULESYNC_CONFIG_SCHEMA_URL, v as McpProcessor, vt as RULESYNC_MCP_SCHEMA_URL, w as CommandsProcessor, wt as RULESYNC_SKILLS_RELATIVE_DIR_PATH, x as RulesyncIgnore, xt as RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH, y as RulesyncMcp, yt as RULESYNC_OVERVIEW_FILE_NAME, z as ErrorCodes } from "../import-Dioh9ca8.js";
2
+ import { $ as removeFile, A as CLAUDECODE_SKILLS_DIR_PATH, At as RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH, B as ErrorCodes, C as RulesyncHooks, Ct as RULESYNC_PERMISSIONS_FILE_NAME, D as CLAUDECODE_LOCAL_RULE_FILE_NAME, Dt as RULESYNC_RELATIVE_DIR_PATH, E as CLAUDECODE_DIR, Et as RULESYNC_PERMISSIONS_SCHEMA_URL, F as ConfigResolver, G as fileExists, H as createTempDirectory, I as findControlCharacter, J as getHomeDirectory, K as findFilesByGlobs, L as ConsoleLogger, M as RulesyncCommandFrontmatterSchema, Mt as formatError, N as stringifyFrontmatter, Nt as ALL_FEATURES, O as CLAUDECODE_MEMORIES_DIR_NAME, Ot as RULESYNC_RULES_RELATIVE_DIR_PATH, P as loadYaml, Pt as ALL_FEATURES_WITH_WILDCARD, Q as removeDirectory, R as JsonLogger, S as HooksProcessor, St as RULESYNC_OVERVIEW_FILE_NAME, T as CODEXCLI_DIR, Tt as RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH, U as directoryExists, V as checkPathTraversal, W as ensureDir, X as listDirectoryFiles, Y as isSymlink, Z as readFileContent, _ as RulesyncPermissions, _t as RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH, a as convertFromTool, at as ToolTargetSchema, b as IgnoreProcessor, bt as RULESYNC_MCP_RELATIVE_FILE_PATH, c as RulesyncRuleFrontmatterSchema, ct as RULESYNC_AIIGNORE_RELATIVE_FILE_PATH, d as RulesyncSubagentFrontmatterSchema, dt as RULESYNC_CONFIG_SCHEMA_URL, et as removeTempDirectory, f as SkillsProcessor, ft as RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH, g as SKILL_FILE_NAME$1, gt as RULESYNC_IGNORE_RELATIVE_FILE_PATH, h as RulesyncSkillFrontmatterSchema, ht as RULESYNC_HOOKS_RELATIVE_FILE_PATH, i as getProcessorRegistryEntry, it as ALL_TOOL_TARGETS_WITH_WILDCARD, j as RulesyncCommand, jt as RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, k as CLAUDECODE_SETTINGS_LOCAL_FILE_NAME, kt as RULESYNC_SKILLS_RELATIVE_DIR_PATH, l as SubagentsProcessor, lt as RULESYNC_COMMANDS_RELATIVE_DIR_PATH, m as RulesyncSkill, mt as RULESYNC_HOOKS_JSONC_FILE_NAME, n as checkRulesyncDirExists, nt as writeFileContent, o as RulesProcessor, ot as MAX_FILE_SIZE, p as getLocalSkillDirNames, pt as RULESYNC_HOOKS_FILE_NAME, q as getFileSize, r as generate, rt as ALL_TOOL_TARGETS, s as RulesyncRule, st as RULESYNC_AIIGNORE_FILE_NAME, t as importFromTool, tt as toPosixPath, u as RulesyncSubagent, ut as RULESYNC_CONFIG_RELATIVE_FILE_PATH, v as McpProcessor, vt as RULESYNC_MCP_FILE_NAME, w as CommandsProcessor, wt as RULESYNC_PERMISSIONS_JSONC_FILE_NAME, x as RulesyncIgnore, xt as RULESYNC_MCP_SCHEMA_URL, y as RulesyncMcp, yt as RULESYNC_MCP_JSONC_FILE_NAME, z as CLIError } from "../import-B02AyWkQ.js";
3
3
  import { Command } from "commander";
4
4
  import { nonnegative, optional, refine, z } from "zod/mini";
5
5
  import * as path$1 from "node:path";
@@ -540,9 +540,9 @@ const FEATURE_PATHS = {
540
540
  subagents: ["subagents"],
541
541
  skills: ["skills"],
542
542
  ignore: [RULESYNC_AIIGNORE_FILE_NAME],
543
- mcp: [RULESYNC_MCP_FILE_NAME],
544
- hooks: [RULESYNC_HOOKS_FILE_NAME],
545
- permissions: [RULESYNC_PERMISSIONS_FILE_NAME]
543
+ mcp: [RULESYNC_MCP_FILE_NAME, RULESYNC_MCP_JSONC_FILE_NAME],
544
+ hooks: [RULESYNC_HOOKS_FILE_NAME, RULESYNC_HOOKS_JSONC_FILE_NAME],
545
+ permissions: [RULESYNC_PERMISSIONS_FILE_NAME, RULESYNC_PERMISSIONS_JSONC_FILE_NAME]
546
546
  };
547
547
  /**
548
548
  * Check if target is a tool target (not rulesync)
@@ -1202,11 +1202,13 @@ const DERIVED_PATHS_NOT_GITIGNORED = /* @__PURE__ */ new Set([
1202
1202
  "**/.antigravity/settings.json",
1203
1203
  "**/.claude/settings.json",
1204
1204
  "**/.claude/settings.local.json",
1205
+ "**/.codex/config.toml",
1205
1206
  "**/.devin/config.json",
1206
1207
  "**/.factory/settings.json",
1207
- "**/.gemini/settings.json",
1208
+ "**/.grok/config.toml",
1209
+ "**/.vibe/config.toml",
1210
+ "**/reasonix.toml",
1208
1211
  "**/.zed/settings.json",
1209
- "**/.warp/settings.toml",
1210
1212
  "**/kilo.json",
1211
1213
  "**/kilo.jsonc",
1212
1214
  "**/opencode.json"
@@ -1222,7 +1224,6 @@ const supportsProject = (factory) => {
1222
1224
  };
1223
1225
  const getProjectPaths = (factory) => factory.class.getSettablePaths({ global: false });
1224
1226
  const pushEntry = (entries, target, feature, entry) => {
1225
- if (DERIVED_PATHS_NOT_GITIGNORED.has(entry)) return;
1226
1227
  entries.push({
1227
1228
  target,
1228
1229
  feature,
@@ -1291,7 +1292,8 @@ const DERIVED_FEATURES = [
1291
1292
  "permissions",
1292
1293
  "ignore"
1293
1294
  ];
1294
- const deriveAllGitignoreEntries = () => DERIVED_FEATURES.flatMap((feature) => deriveFeatureGitignoreEntries(feature));
1295
+ const deriveAllGitignoreEntriesUnfiltered = () => DERIVED_FEATURES.flatMap((feature) => deriveFeatureGitignoreEntries(feature));
1296
+ const deriveAllGitignoreEntries = () => deriveAllGitignoreEntriesUnfiltered().filter((tag) => !DERIVED_PATHS_NOT_GITIGNORED.has(tag.entry));
1295
1297
  //#endregion
1296
1298
  //#region src/cli/commands/gitignore-entries.ts
1297
1299
  const normalizeGitignoreEntryTargets = (target) => {
@@ -1423,6 +1425,11 @@ const GITIGNORE_ENTRY_REGISTRY = [
1423
1425
  target: "codexcli",
1424
1426
  feature: "ignore",
1425
1427
  entry: "**/.codexignore"
1428
+ },
1429
+ {
1430
+ target: "codexcli",
1431
+ feature: "permissions",
1432
+ entry: `**/${CODEXCLI_DIR}/rules/`
1426
1433
  }
1427
1434
  ],
1428
1435
  ...deriveAllGitignoreEntries(),
@@ -1497,50 +1504,103 @@ const resolveGitignoreEntries = (params) => {
1497
1504
  //#endregion
1498
1505
  //#region src/cli/commands/gitignore.ts
1499
1506
  const RULESYNC_HEADER = "# Generated by Rulesync";
1507
+ const RULESYNC_FOOTER = "# End of Rulesync";
1500
1508
  const LEGACY_RULESYNC_HEADER = "# Generated by rulesync - AI tool configuration files";
1501
1509
  const isRulesyncHeader = (line) => {
1502
1510
  const trimmed = line.trim();
1503
1511
  return trimmed === RULESYNC_HEADER || trimmed === LEGACY_RULESYNC_HEADER;
1504
1512
  };
1513
+ const isRulesyncFooter = (line) => {
1514
+ return line.trim() === RULESYNC_FOOTER;
1515
+ };
1505
1516
  const isRulesyncEntry = (line) => {
1506
1517
  const trimmed = line.trim();
1507
- if (trimmed === "" || isRulesyncHeader(line)) return false;
1518
+ if (trimmed === "" || isRulesyncHeader(line) || isRulesyncFooter(line)) return false;
1508
1519
  return ALL_GITIGNORE_ENTRIES.includes(trimmed);
1509
1520
  };
1521
+ const findRulesyncFooterIndex = (lines, start) => {
1522
+ for (let index = start; index < lines.length; index++) {
1523
+ const line = lines[index] ?? "";
1524
+ if (isRulesyncFooter(line)) return index;
1525
+ if (isRulesyncHeader(line)) return -1;
1526
+ }
1527
+ return -1;
1528
+ };
1529
+ const skipLegacyRulesyncBlock = (lines, headerIndex) => {
1530
+ let index = headerIndex + 1;
1531
+ let consecutiveEmptyLines = 0;
1532
+ while (index < lines.length) {
1533
+ const line = lines[index] ?? "";
1534
+ if (line.trim() === "") {
1535
+ consecutiveEmptyLines++;
1536
+ index++;
1537
+ if (consecutiveEmptyLines >= 2) break;
1538
+ continue;
1539
+ }
1540
+ if (isRulesyncEntry(line)) {
1541
+ consecutiveEmptyLines = 0;
1542
+ index++;
1543
+ continue;
1544
+ }
1545
+ break;
1546
+ }
1547
+ return index;
1548
+ };
1510
1549
  const removeExistingRulesyncEntries = (content) => {
1511
1550
  const lines = content.split("\n");
1512
1551
  const filteredLines = [];
1513
- let inRulesyncBlock = false;
1514
- let consecutiveEmptyLines = 0;
1515
- for (const line of lines) {
1516
- const trimmed = line.trim();
1552
+ let index = 0;
1553
+ while (index < lines.length) {
1554
+ const line = lines[index] ?? "";
1517
1555
  if (isRulesyncHeader(line)) {
1518
- inRulesyncBlock = true;
1519
- continue;
1520
- }
1521
- if (inRulesyncBlock) {
1522
- if (trimmed === "") {
1523
- consecutiveEmptyLines++;
1524
- if (consecutiveEmptyLines >= 2) {
1525
- inRulesyncBlock = false;
1526
- consecutiveEmptyLines = 0;
1527
- }
1528
- continue;
1529
- }
1530
- if (isRulesyncEntry(line)) {
1531
- consecutiveEmptyLines = 0;
1556
+ const footerIndex = findRulesyncFooterIndex(lines, index + 1);
1557
+ if (footerIndex !== -1) {
1558
+ index = footerIndex + 1;
1532
1559
  continue;
1533
1560
  }
1534
- inRulesyncBlock = false;
1535
- consecutiveEmptyLines = 0;
1561
+ index = skipLegacyRulesyncBlock(lines, index);
1562
+ continue;
1563
+ }
1564
+ if (isRulesyncEntry(line)) {
1565
+ index++;
1566
+ continue;
1536
1567
  }
1537
- if (isRulesyncEntry(line)) continue;
1538
1568
  filteredLines.push(line);
1569
+ index++;
1539
1570
  }
1540
1571
  let result = filteredLines.join("\n");
1541
1572
  while (result.endsWith("\n\n")) result = result.slice(0, -1);
1542
1573
  return result;
1543
1574
  };
1575
+ const extractRulesyncManagedEntries = (content) => {
1576
+ const lines = content.split("\n");
1577
+ const managed = [];
1578
+ let index = 0;
1579
+ const collectBlockLines = (start, end) => {
1580
+ for (const blockLine of lines.slice(start, end)) {
1581
+ const trimmed = blockLine.trim();
1582
+ if (trimmed !== "") managed.push(trimmed);
1583
+ }
1584
+ };
1585
+ while (index < lines.length) {
1586
+ const line = lines[index] ?? "";
1587
+ if (isRulesyncHeader(line)) {
1588
+ const footerIndex = findRulesyncFooterIndex(lines, index + 1);
1589
+ if (footerIndex !== -1) {
1590
+ collectBlockLines(index + 1, footerIndex);
1591
+ index = footerIndex + 1;
1592
+ continue;
1593
+ }
1594
+ const legacyEnd = skipLegacyRulesyncBlock(lines, index);
1595
+ collectBlockLines(index + 1, legacyEnd);
1596
+ index = legacyEnd;
1597
+ continue;
1598
+ }
1599
+ if (isRulesyncEntry(line)) managed.push(line.trim());
1600
+ index++;
1601
+ }
1602
+ return managed;
1603
+ };
1544
1604
  const groupEntriesByDestination = ({ entries, resolveDestination }) => {
1545
1605
  const gitignore = /* @__PURE__ */ new Set();
1546
1606
  const gitattributes = /* @__PURE__ */ new Set();
@@ -1577,21 +1637,29 @@ const gitignoreCommand = async (logger, options) => {
1577
1637
  let content = "";
1578
1638
  if (await fileExists(filePath)) content = await readFileContent(filePath);
1579
1639
  const cleanedContent = removeExistingRulesyncEntries(content);
1580
- const existingEntries = new Set(content.split("\n").map((line) => line.trim()).filter((line) => line !== "" && !isRulesyncHeader(line)));
1640
+ const entrySet = new Set(entries);
1641
+ const entriesRemoved = [...new Set(extractRulesyncManagedEntries(content).filter((entry) => !entrySet.has(entry)))];
1642
+ const existingEntries = new Set(content.split("\n").map((line) => line.trim()).filter((line) => line !== "" && !isRulesyncHeader(line) && !isRulesyncFooter(line)));
1581
1643
  const alreadyExistedEntries = entries.filter((entry) => existingEntries.has(entry));
1582
1644
  const entriesToAdd = entries.filter((entry) => !existingEntries.has(entry));
1583
- const rulesyncBlock = [RULESYNC_HEADER, ...entries].join("\n");
1645
+ const rulesyncBlock = [
1646
+ RULESYNC_HEADER,
1647
+ ...entries,
1648
+ RULESYNC_FOOTER
1649
+ ].join("\n");
1584
1650
  const newContent = entries.length === 0 ? cleanedContent.trim() ? `${cleanedContent.trimEnd()}\n` : "" : cleanedContent.trim() ? `${cleanedContent.trimEnd()}\n\n${rulesyncBlock}\n` : `${rulesyncBlock}\n`;
1585
1651
  if (content === newContent) return {
1586
1652
  updated: false,
1587
1653
  alreadyExistedEntries,
1588
- entriesToAdd: []
1654
+ entriesToAdd: [],
1655
+ entriesRemoved: []
1589
1656
  };
1590
1657
  await writeFileContent(filePath, newContent);
1591
1658
  return {
1592
1659
  updated: true,
1593
1660
  alreadyExistedEntries,
1594
- entriesToAdd
1661
+ entriesToAdd,
1662
+ entriesRemoved
1595
1663
  };
1596
1664
  };
1597
1665
  const gitignoreResult = await updateRulesyncFile({
@@ -1617,6 +1685,12 @@ const gitignoreCommand = async (logger, options) => {
1617
1685
  logger.captureData("gitignorePath", gitignorePath);
1618
1686
  logger.captureData("gitattributesPath", gitattributesPath);
1619
1687
  logger.captureData("alreadyExisted", [...gitignoreResult.alreadyExistedEntries, ...gitattributesResult.alreadyExistedEntries]);
1688
+ logger.captureData("entriesRemoved", gitignoreResult.entriesRemoved);
1689
+ }
1690
+ if (gitignoreResult.entriesRemoved.length > 0) {
1691
+ logger.warn("The following entries were removed from the rulesync-managed block in .gitignore and are no longer gitignored by rulesync:");
1692
+ for (const entry of gitignoreResult.entriesRemoved) logger.warn(` ${entry}`);
1693
+ logger.warn("Review these paths before committing — user-managed settings files may contain secrets.");
1620
1694
  }
1621
1695
  if (gitignoreResult.updated) logger.success("Updated .gitignore with rulesync entries:");
1622
1696
  else logger.success(".gitignore is already up to date");
@@ -1713,7 +1787,8 @@ async function createConfigFile() {
1713
1787
  "commands",
1714
1788
  "subagents",
1715
1789
  "skills",
1716
- "hooks"
1790
+ "hooks",
1791
+ "permissions"
1717
1792
  ],
1718
1793
  outputRoots: ["."],
1719
1794
  delete: true,
@@ -1857,6 +1932,25 @@ Keep the summary concise and ready to reuse in future tasks.`
1857
1932
  ]
1858
1933
  }
1859
1934
  }
1935
+ ` };
1936
+ const samplePermissionsFile = { content: `{
1937
+ "$schema": "${RULESYNC_PERMISSIONS_SCHEMA_URL}",
1938
+ "permission": {
1939
+ "bash": {
1940
+ "git status": "allow",
1941
+ "git diff": "allow",
1942
+ "ls *": "allow",
1943
+ "rm -rf *": "deny",
1944
+ "*": "ask"
1945
+ },
1946
+ "edit": {
1947
+ "src/**": "allow"
1948
+ },
1949
+ "read": {
1950
+ ".env": "deny"
1951
+ }
1952
+ }
1953
+ }
1860
1954
  ` };
1861
1955
  const rulePaths = RulesyncRule.getSettablePaths();
1862
1956
  const mcpPaths = RulesyncMcp.getSettablePaths();
@@ -1865,6 +1959,7 @@ Keep the summary concise and ready to reuse in future tasks.`
1865
1959
  const skillPaths = RulesyncSkill.getSettablePaths();
1866
1960
  const ignorePaths = RulesyncIgnore.getSettablePaths();
1867
1961
  const hooksPaths = RulesyncHooks.getSettablePaths();
1962
+ const permissionsPaths = RulesyncPermissions.getSettablePaths();
1868
1963
  await ensureDir(rulePaths.recommended.relativeDirPath);
1869
1964
  await ensureDir(mcpPaths.recommended.relativeDirPath);
1870
1965
  await ensureDir(commandPaths.relativeDirPath);
@@ -1887,6 +1982,8 @@ Keep the summary concise and ready to reuse in future tasks.`
1887
1982
  results.push(await writeIfNotExists(ignoreFilepath, sampleIgnoreFile.content));
1888
1983
  const hooksFilepath = join(hooksPaths.relativeDirPath, hooksPaths.relativeFilePath);
1889
1984
  results.push(await writeIfNotExists(hooksFilepath, sampleHooksFile.content));
1985
+ const permissionsFilepath = join(permissionsPaths.relativeDirPath, permissionsPaths.relativeFilePath);
1986
+ results.push(await writeIfNotExists(permissionsFilepath, samplePermissionsFile.content));
1890
1987
  return results;
1891
1988
  }
1892
1989
  async function writeIfNotExists(path, content) {
@@ -1928,7 +2025,7 @@ async function initCommand(logger) {
1928
2025
  }
1929
2026
  logger.success("rulesync initialized successfully!");
1930
2027
  logger.info("Next steps:");
1931
- logger.info(`1. Edit ${RULESYNC_RELATIVE_DIR_PATH}/**/*.md, ${RULESYNC_RELATIVE_DIR_PATH}/skills/*/${SKILL_FILE_NAME$1}, ${RULESYNC_MCP_RELATIVE_FILE_PATH}, ${RULESYNC_HOOKS_RELATIVE_FILE_PATH} and ${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}`);
2028
+ logger.info(`1. Edit ${RULESYNC_RELATIVE_DIR_PATH}/**/*.md, ${RULESYNC_RELATIVE_DIR_PATH}/skills/*/${SKILL_FILE_NAME$1}, ${RULESYNC_MCP_RELATIVE_FILE_PATH}, ${RULESYNC_HOOKS_RELATIVE_FILE_PATH}, ${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH} and ${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}`);
1932
2029
  logger.info("2. Run 'rulesync generate' to create configuration files");
1933
2030
  }
1934
2031
  //#endregion
@@ -4738,6 +4835,8 @@ async function putHooksFile({ content }) {
4738
4835
  try {
4739
4836
  const outputRoot = process.cwd();
4740
4837
  const paths = RulesyncHooks.getSettablePaths();
4838
+ const jsoncRelativePath = join(paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath);
4839
+ if (await fileExists(join(outputRoot, jsoncRelativePath))) throw new Error(`${jsoncRelativePath} exists and takes precedence over ${RULESYNC_HOOKS_RELATIVE_FILE_PATH}. Edit ${jsoncRelativePath} directly instead.`);
4741
4840
  const relativeDirPath = paths.relativeDirPath;
4742
4841
  const relativeFilePath = paths.relativeFilePath;
4743
4842
  const fullPath = join(outputRoot, relativeDirPath, relativeFilePath);
@@ -4766,6 +4865,7 @@ async function deleteHooksFile() {
4766
4865
  const outputRoot = process.cwd();
4767
4866
  const paths = RulesyncHooks.getSettablePaths();
4768
4867
  await removeFile(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath));
4868
+ await removeFile(join(outputRoot, paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath));
4769
4869
  return { relativePathFromCwd: join(paths.relativeDirPath, paths.relativeFilePath) };
4770
4870
  } catch (error) {
4771
4871
  throw new Error(`Failed to delete hooks file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${formatError(error)}`, { cause: error });
@@ -5016,6 +5116,8 @@ async function putMcpFile({ content }) {
5016
5116
  try {
5017
5117
  const outputRoot = process.cwd();
5018
5118
  const paths = RulesyncMcp.getSettablePaths();
5119
+ const jsoncRelativePath = join(paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath);
5120
+ if (await fileExists(join(outputRoot, jsoncRelativePath))) throw new Error(`${jsoncRelativePath} exists and takes precedence over ${RULESYNC_MCP_RELATIVE_FILE_PATH}. Edit ${jsoncRelativePath} directly instead.`);
5019
5121
  const relativeDirPath = paths.recommended.relativeDirPath;
5020
5122
  const relativeFilePath = paths.recommended.relativeFilePath;
5021
5123
  const fullPath = join(outputRoot, relativeDirPath, relativeFilePath);
@@ -5046,6 +5148,7 @@ async function deleteMcpFile() {
5046
5148
  const recommendedPath = join(outputRoot, paths.recommended.relativeDirPath, paths.recommended.relativeFilePath);
5047
5149
  const legacyPath = join(outputRoot, paths.legacy.relativeDirPath, paths.legacy.relativeFilePath);
5048
5150
  await removeFile(recommendedPath);
5151
+ await removeFile(join(outputRoot, paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath));
5049
5152
  await removeFile(legacyPath);
5050
5153
  return { relativePathFromCwd: join(paths.recommended.relativeDirPath, paths.recommended.relativeFilePath) };
5051
5154
  } catch (error) {
@@ -5122,6 +5225,8 @@ async function putPermissionsFile({ content }) {
5122
5225
  try {
5123
5226
  const outputRoot = process.cwd();
5124
5227
  const paths = RulesyncPermissions.getSettablePaths();
5228
+ const jsoncRelativePath = join(paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath);
5229
+ if (await fileExists(join(outputRoot, jsoncRelativePath))) throw new Error(`${jsoncRelativePath} exists and takes precedence over ${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}. Edit ${jsoncRelativePath} directly instead.`);
5125
5230
  const relativeDirPath = paths.relativeDirPath;
5126
5231
  const relativeFilePath = paths.relativeFilePath;
5127
5232
  const fullPath = join(outputRoot, relativeDirPath, relativeFilePath);
@@ -5150,6 +5255,7 @@ async function deletePermissionsFile() {
5150
5255
  const outputRoot = process.cwd();
5151
5256
  const paths = RulesyncPermissions.getSettablePaths();
5152
5257
  await removeFile(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath));
5258
+ await removeFile(join(outputRoot, paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath));
5153
5259
  return { relativePathFromCwd: join(paths.relativeDirPath, paths.relativeFilePath) };
5154
5260
  } catch (error) {
5155
5261
  throw new Error(`Failed to delete permissions file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}): ${formatError(error)}`, { cause: error });
@@ -6448,7 +6554,7 @@ function wrapCommand$1({ name, errorCode, handler, getVersion, loggerFactory = c
6448
6554
  }
6449
6555
  //#endregion
6450
6556
  //#region src/cli/index.ts
6451
- const getVersion = () => "9.8.0";
6557
+ const getVersion = () => "10.1.0";
6452
6558
  function wrapCommand(name, errorCode, handler) {
6453
6559
  return wrapCommand$1({
6454
6560
  name,