rulesync 9.7.0 → 10.0.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 +1 -1
- package/dist/cli/index.cjs +96 -31
- package/dist/cli/index.js +96 -31
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-DC9T1JlQ.js → import-B02AyWkQ.js} +960 -163
- package/dist/import-B02AyWkQ.js.map +1 -0
- package/dist/{import-D5f_X4-A.cjs → import-Dcj5HiOE.cjs} +998 -171
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/import-DC9T1JlQ.js.map +0 -1
package/README.md
CHANGED
|
@@ -95,7 +95,7 @@ The tables below show whether each tool supports a given feature (✅ = supporte
|
|
|
95
95
|
| GitHub Copilot CLI | ✅ | | ✅ | | ✅ | ✅ | ✅ | |
|
|
96
96
|
| Goose | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
97
97
|
| Hermes Agent | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
98
|
-
| Grok CLI | ✅ | | ✅ | | ✅ | ✅ |
|
|
98
|
+
| Grok CLI | ✅ | | ✅ | | ✅ | ✅ | ✅ | ✅ |
|
|
99
99
|
| Cursor | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
100
100
|
| deepagents-cli | ✅ | | ✅ | | ✅ | ✅ | ✅ | |
|
|
101
101
|
| Factory Droid | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
package/dist/cli/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_import = require("../import-
|
|
2
|
+
const require_import = require("../import-Dcj5HiOE.cjs");
|
|
3
3
|
let commander = require("commander");
|
|
4
4
|
let zod_mini = require("zod/mini");
|
|
5
5
|
let node_path = require("node:path");
|
|
@@ -542,9 +542,9 @@ const FEATURE_PATHS = {
|
|
|
542
542
|
subagents: ["subagents"],
|
|
543
543
|
skills: ["skills"],
|
|
544
544
|
ignore: [require_import.RULESYNC_AIIGNORE_FILE_NAME],
|
|
545
|
-
mcp: [require_import.RULESYNC_MCP_FILE_NAME],
|
|
546
|
-
hooks: [require_import.RULESYNC_HOOKS_FILE_NAME],
|
|
547
|
-
permissions: [require_import.RULESYNC_PERMISSIONS_FILE_NAME]
|
|
545
|
+
mcp: [require_import.RULESYNC_MCP_FILE_NAME, require_import.RULESYNC_MCP_JSONC_FILE_NAME],
|
|
546
|
+
hooks: [require_import.RULESYNC_HOOKS_FILE_NAME, require_import.RULESYNC_HOOKS_JSONC_FILE_NAME],
|
|
547
|
+
permissions: [require_import.RULESYNC_PERMISSIONS_FILE_NAME, require_import.RULESYNC_PERMISSIONS_JSONC_FILE_NAME]
|
|
548
548
|
};
|
|
549
549
|
/**
|
|
550
550
|
* Check if target is a tool target (not rulesync)
|
|
@@ -1425,6 +1425,11 @@ const GITIGNORE_ENTRY_REGISTRY = [
|
|
|
1425
1425
|
target: "codexcli",
|
|
1426
1426
|
feature: "ignore",
|
|
1427
1427
|
entry: "**/.codexignore"
|
|
1428
|
+
},
|
|
1429
|
+
{
|
|
1430
|
+
target: "codexcli",
|
|
1431
|
+
feature: "permissions",
|
|
1432
|
+
entry: `**/${require_import.CODEXCLI_DIR}/rules/`
|
|
1428
1433
|
}
|
|
1429
1434
|
],
|
|
1430
1435
|
...deriveAllGitignoreEntries(),
|
|
@@ -1499,45 +1504,69 @@ const resolveGitignoreEntries = (params) => {
|
|
|
1499
1504
|
//#endregion
|
|
1500
1505
|
//#region src/cli/commands/gitignore.ts
|
|
1501
1506
|
const RULESYNC_HEADER = "# Generated by Rulesync";
|
|
1507
|
+
const RULESYNC_FOOTER = "# End of Rulesync";
|
|
1502
1508
|
const LEGACY_RULESYNC_HEADER = "# Generated by rulesync - AI tool configuration files";
|
|
1503
1509
|
const isRulesyncHeader = (line) => {
|
|
1504
1510
|
const trimmed = line.trim();
|
|
1505
1511
|
return trimmed === RULESYNC_HEADER || trimmed === LEGACY_RULESYNC_HEADER;
|
|
1506
1512
|
};
|
|
1513
|
+
const isRulesyncFooter = (line) => {
|
|
1514
|
+
return line.trim() === RULESYNC_FOOTER;
|
|
1515
|
+
};
|
|
1507
1516
|
const isRulesyncEntry = (line) => {
|
|
1508
1517
|
const trimmed = line.trim();
|
|
1509
|
-
if (trimmed === "" || isRulesyncHeader(line)) return false;
|
|
1518
|
+
if (trimmed === "" || isRulesyncHeader(line) || isRulesyncFooter(line)) return false;
|
|
1510
1519
|
return ALL_GITIGNORE_ENTRIES.includes(trimmed);
|
|
1511
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
|
+
};
|
|
1512
1549
|
const removeExistingRulesyncEntries = (content) => {
|
|
1513
1550
|
const lines = content.split("\n");
|
|
1514
1551
|
const filteredLines = [];
|
|
1515
|
-
let
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
const trimmed = line.trim();
|
|
1552
|
+
let index = 0;
|
|
1553
|
+
while (index < lines.length) {
|
|
1554
|
+
const line = lines[index] ?? "";
|
|
1519
1555
|
if (isRulesyncHeader(line)) {
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
if (inRulesyncBlock) {
|
|
1524
|
-
if (trimmed === "") {
|
|
1525
|
-
consecutiveEmptyLines++;
|
|
1526
|
-
if (consecutiveEmptyLines >= 2) {
|
|
1527
|
-
inRulesyncBlock = false;
|
|
1528
|
-
consecutiveEmptyLines = 0;
|
|
1529
|
-
}
|
|
1556
|
+
const footerIndex = findRulesyncFooterIndex(lines, index + 1);
|
|
1557
|
+
if (footerIndex !== -1) {
|
|
1558
|
+
index = footerIndex + 1;
|
|
1530
1559
|
continue;
|
|
1531
1560
|
}
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1561
|
+
index = skipLegacyRulesyncBlock(lines, index);
|
|
1562
|
+
continue;
|
|
1563
|
+
}
|
|
1564
|
+
if (isRulesyncEntry(line)) {
|
|
1565
|
+
index++;
|
|
1566
|
+
continue;
|
|
1538
1567
|
}
|
|
1539
|
-
if (isRulesyncEntry(line)) continue;
|
|
1540
1568
|
filteredLines.push(line);
|
|
1569
|
+
index++;
|
|
1541
1570
|
}
|
|
1542
1571
|
let result = filteredLines.join("\n");
|
|
1543
1572
|
while (result.endsWith("\n\n")) result = result.slice(0, -1);
|
|
@@ -1579,10 +1608,14 @@ const gitignoreCommand = async (logger, options) => {
|
|
|
1579
1608
|
let content = "";
|
|
1580
1609
|
if (await require_import.fileExists(filePath)) content = await require_import.readFileContent(filePath);
|
|
1581
1610
|
const cleanedContent = removeExistingRulesyncEntries(content);
|
|
1582
|
-
const existingEntries = new Set(content.split("\n").map((line) => line.trim()).filter((line) => line !== "" && !isRulesyncHeader(line)));
|
|
1611
|
+
const existingEntries = new Set(content.split("\n").map((line) => line.trim()).filter((line) => line !== "" && !isRulesyncHeader(line) && !isRulesyncFooter(line)));
|
|
1583
1612
|
const alreadyExistedEntries = entries.filter((entry) => existingEntries.has(entry));
|
|
1584
1613
|
const entriesToAdd = entries.filter((entry) => !existingEntries.has(entry));
|
|
1585
|
-
const rulesyncBlock = [
|
|
1614
|
+
const rulesyncBlock = [
|
|
1615
|
+
RULESYNC_HEADER,
|
|
1616
|
+
...entries,
|
|
1617
|
+
RULESYNC_FOOTER
|
|
1618
|
+
].join("\n");
|
|
1586
1619
|
const newContent = entries.length === 0 ? cleanedContent.trim() ? `${cleanedContent.trimEnd()}\n` : "" : cleanedContent.trim() ? `${cleanedContent.trimEnd()}\n\n${rulesyncBlock}\n` : `${rulesyncBlock}\n`;
|
|
1587
1620
|
if (content === newContent) return {
|
|
1588
1621
|
updated: false,
|
|
@@ -1715,7 +1748,8 @@ async function createConfigFile() {
|
|
|
1715
1748
|
"commands",
|
|
1716
1749
|
"subagents",
|
|
1717
1750
|
"skills",
|
|
1718
|
-
"hooks"
|
|
1751
|
+
"hooks",
|
|
1752
|
+
"permissions"
|
|
1719
1753
|
],
|
|
1720
1754
|
outputRoots: ["."],
|
|
1721
1755
|
delete: true,
|
|
@@ -1859,6 +1893,25 @@ Keep the summary concise and ready to reuse in future tasks.`
|
|
|
1859
1893
|
]
|
|
1860
1894
|
}
|
|
1861
1895
|
}
|
|
1896
|
+
` };
|
|
1897
|
+
const samplePermissionsFile = { content: `{
|
|
1898
|
+
"$schema": "${require_import.RULESYNC_PERMISSIONS_SCHEMA_URL}",
|
|
1899
|
+
"permission": {
|
|
1900
|
+
"bash": {
|
|
1901
|
+
"git status": "allow",
|
|
1902
|
+
"git diff": "allow",
|
|
1903
|
+
"ls *": "allow",
|
|
1904
|
+
"rm -rf *": "deny",
|
|
1905
|
+
"*": "ask"
|
|
1906
|
+
},
|
|
1907
|
+
"edit": {
|
|
1908
|
+
"src/**": "allow"
|
|
1909
|
+
},
|
|
1910
|
+
"read": {
|
|
1911
|
+
".env": "deny"
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1862
1915
|
` };
|
|
1863
1916
|
const rulePaths = require_import.RulesyncRule.getSettablePaths();
|
|
1864
1917
|
const mcpPaths = require_import.RulesyncMcp.getSettablePaths();
|
|
@@ -1867,6 +1920,7 @@ Keep the summary concise and ready to reuse in future tasks.`
|
|
|
1867
1920
|
const skillPaths = require_import.RulesyncSkill.getSettablePaths();
|
|
1868
1921
|
const ignorePaths = require_import.RulesyncIgnore.getSettablePaths();
|
|
1869
1922
|
const hooksPaths = require_import.RulesyncHooks.getSettablePaths();
|
|
1923
|
+
const permissionsPaths = require_import.RulesyncPermissions.getSettablePaths();
|
|
1870
1924
|
await require_import.ensureDir(rulePaths.recommended.relativeDirPath);
|
|
1871
1925
|
await require_import.ensureDir(mcpPaths.recommended.relativeDirPath);
|
|
1872
1926
|
await require_import.ensureDir(commandPaths.relativeDirPath);
|
|
@@ -1889,6 +1943,8 @@ Keep the summary concise and ready to reuse in future tasks.`
|
|
|
1889
1943
|
results.push(await writeIfNotExists(ignoreFilepath, sampleIgnoreFile.content));
|
|
1890
1944
|
const hooksFilepath = (0, node_path.join)(hooksPaths.relativeDirPath, hooksPaths.relativeFilePath);
|
|
1891
1945
|
results.push(await writeIfNotExists(hooksFilepath, sampleHooksFile.content));
|
|
1946
|
+
const permissionsFilepath = (0, node_path.join)(permissionsPaths.relativeDirPath, permissionsPaths.relativeFilePath);
|
|
1947
|
+
results.push(await writeIfNotExists(permissionsFilepath, samplePermissionsFile.content));
|
|
1892
1948
|
return results;
|
|
1893
1949
|
}
|
|
1894
1950
|
async function writeIfNotExists(path, content) {
|
|
@@ -1930,7 +1986,7 @@ async function initCommand(logger) {
|
|
|
1930
1986
|
}
|
|
1931
1987
|
logger.success("rulesync initialized successfully!");
|
|
1932
1988
|
logger.info("Next steps:");
|
|
1933
|
-
logger.info(`1. Edit ${require_import.RULESYNC_RELATIVE_DIR_PATH}/**/*.md, ${require_import.RULESYNC_RELATIVE_DIR_PATH}/skills/*/${require_import.SKILL_FILE_NAME}, ${require_import.RULESYNC_MCP_RELATIVE_FILE_PATH}, ${require_import.RULESYNC_HOOKS_RELATIVE_FILE_PATH} and ${require_import.RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}`);
|
|
1989
|
+
logger.info(`1. Edit ${require_import.RULESYNC_RELATIVE_DIR_PATH}/**/*.md, ${require_import.RULESYNC_RELATIVE_DIR_PATH}/skills/*/${require_import.SKILL_FILE_NAME}, ${require_import.RULESYNC_MCP_RELATIVE_FILE_PATH}, ${require_import.RULESYNC_HOOKS_RELATIVE_FILE_PATH}, ${require_import.RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH} and ${require_import.RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}`);
|
|
1934
1990
|
logger.info("2. Run 'rulesync generate' to create configuration files");
|
|
1935
1991
|
}
|
|
1936
1992
|
//#endregion
|
|
@@ -4740,6 +4796,8 @@ async function putHooksFile({ content }) {
|
|
|
4740
4796
|
try {
|
|
4741
4797
|
const outputRoot = process.cwd();
|
|
4742
4798
|
const paths = require_import.RulesyncHooks.getSettablePaths();
|
|
4799
|
+
const jsoncRelativePath = (0, node_path.join)(paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath);
|
|
4800
|
+
if (await require_import.fileExists((0, node_path.join)(outputRoot, jsoncRelativePath))) throw new Error(`${jsoncRelativePath} exists and takes precedence over ${require_import.RULESYNC_HOOKS_RELATIVE_FILE_PATH}. Edit ${jsoncRelativePath} directly instead.`);
|
|
4743
4801
|
const relativeDirPath = paths.relativeDirPath;
|
|
4744
4802
|
const relativeFilePath = paths.relativeFilePath;
|
|
4745
4803
|
const fullPath = (0, node_path.join)(outputRoot, relativeDirPath, relativeFilePath);
|
|
@@ -4768,6 +4826,7 @@ async function deleteHooksFile() {
|
|
|
4768
4826
|
const outputRoot = process.cwd();
|
|
4769
4827
|
const paths = require_import.RulesyncHooks.getSettablePaths();
|
|
4770
4828
|
await require_import.removeFile((0, node_path.join)(outputRoot, paths.relativeDirPath, paths.relativeFilePath));
|
|
4829
|
+
await require_import.removeFile((0, node_path.join)(outputRoot, paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath));
|
|
4771
4830
|
return { relativePathFromCwd: (0, node_path.join)(paths.relativeDirPath, paths.relativeFilePath) };
|
|
4772
4831
|
} catch (error) {
|
|
4773
4832
|
throw new Error(`Failed to delete hooks file (${require_import.RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${require_import.formatError(error)}`, { cause: error });
|
|
@@ -5018,6 +5077,8 @@ async function putMcpFile({ content }) {
|
|
|
5018
5077
|
try {
|
|
5019
5078
|
const outputRoot = process.cwd();
|
|
5020
5079
|
const paths = require_import.RulesyncMcp.getSettablePaths();
|
|
5080
|
+
const jsoncRelativePath = (0, node_path.join)(paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath);
|
|
5081
|
+
if (await require_import.fileExists((0, node_path.join)(outputRoot, jsoncRelativePath))) throw new Error(`${jsoncRelativePath} exists and takes precedence over ${require_import.RULESYNC_MCP_RELATIVE_FILE_PATH}. Edit ${jsoncRelativePath} directly instead.`);
|
|
5021
5082
|
const relativeDirPath = paths.recommended.relativeDirPath;
|
|
5022
5083
|
const relativeFilePath = paths.recommended.relativeFilePath;
|
|
5023
5084
|
const fullPath = (0, node_path.join)(outputRoot, relativeDirPath, relativeFilePath);
|
|
@@ -5048,6 +5109,7 @@ async function deleteMcpFile() {
|
|
|
5048
5109
|
const recommendedPath = (0, node_path.join)(outputRoot, paths.recommended.relativeDirPath, paths.recommended.relativeFilePath);
|
|
5049
5110
|
const legacyPath = (0, node_path.join)(outputRoot, paths.legacy.relativeDirPath, paths.legacy.relativeFilePath);
|
|
5050
5111
|
await require_import.removeFile(recommendedPath);
|
|
5112
|
+
await require_import.removeFile((0, node_path.join)(outputRoot, paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath));
|
|
5051
5113
|
await require_import.removeFile(legacyPath);
|
|
5052
5114
|
return { relativePathFromCwd: (0, node_path.join)(paths.recommended.relativeDirPath, paths.recommended.relativeFilePath) };
|
|
5053
5115
|
} catch (error) {
|
|
@@ -5124,6 +5186,8 @@ async function putPermissionsFile({ content }) {
|
|
|
5124
5186
|
try {
|
|
5125
5187
|
const outputRoot = process.cwd();
|
|
5126
5188
|
const paths = require_import.RulesyncPermissions.getSettablePaths();
|
|
5189
|
+
const jsoncRelativePath = (0, node_path.join)(paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath);
|
|
5190
|
+
if (await require_import.fileExists((0, node_path.join)(outputRoot, jsoncRelativePath))) throw new Error(`${jsoncRelativePath} exists and takes precedence over ${require_import.RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}. Edit ${jsoncRelativePath} directly instead.`);
|
|
5127
5191
|
const relativeDirPath = paths.relativeDirPath;
|
|
5128
5192
|
const relativeFilePath = paths.relativeFilePath;
|
|
5129
5193
|
const fullPath = (0, node_path.join)(outputRoot, relativeDirPath, relativeFilePath);
|
|
@@ -5152,6 +5216,7 @@ async function deletePermissionsFile() {
|
|
|
5152
5216
|
const outputRoot = process.cwd();
|
|
5153
5217
|
const paths = require_import.RulesyncPermissions.getSettablePaths();
|
|
5154
5218
|
await require_import.removeFile((0, node_path.join)(outputRoot, paths.relativeDirPath, paths.relativeFilePath));
|
|
5219
|
+
await require_import.removeFile((0, node_path.join)(outputRoot, paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath));
|
|
5155
5220
|
return { relativePathFromCwd: (0, node_path.join)(paths.relativeDirPath, paths.relativeFilePath) };
|
|
5156
5221
|
} catch (error) {
|
|
5157
5222
|
throw new Error(`Failed to delete permissions file (${require_import.RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}): ${require_import.formatError(error)}`, { cause: error });
|
|
@@ -6450,7 +6515,7 @@ function wrapCommand$1({ name, errorCode, handler, getVersion, loggerFactory = c
|
|
|
6450
6515
|
}
|
|
6451
6516
|
//#endregion
|
|
6452
6517
|
//#region src/cli/index.ts
|
|
6453
|
-
const getVersion = () => "
|
|
6518
|
+
const getVersion = () => "10.0.0";
|
|
6454
6519
|
function wrapCommand(name, errorCode, handler) {
|
|
6455
6520
|
return wrapCommand$1({
|
|
6456
6521
|
name,
|
package/dist/cli/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as
|
|
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)
|
|
@@ -1423,6 +1423,11 @@ const GITIGNORE_ENTRY_REGISTRY = [
|
|
|
1423
1423
|
target: "codexcli",
|
|
1424
1424
|
feature: "ignore",
|
|
1425
1425
|
entry: "**/.codexignore"
|
|
1426
|
+
},
|
|
1427
|
+
{
|
|
1428
|
+
target: "codexcli",
|
|
1429
|
+
feature: "permissions",
|
|
1430
|
+
entry: `**/${CODEXCLI_DIR}/rules/`
|
|
1426
1431
|
}
|
|
1427
1432
|
],
|
|
1428
1433
|
...deriveAllGitignoreEntries(),
|
|
@@ -1497,45 +1502,69 @@ const resolveGitignoreEntries = (params) => {
|
|
|
1497
1502
|
//#endregion
|
|
1498
1503
|
//#region src/cli/commands/gitignore.ts
|
|
1499
1504
|
const RULESYNC_HEADER = "# Generated by Rulesync";
|
|
1505
|
+
const RULESYNC_FOOTER = "# End of Rulesync";
|
|
1500
1506
|
const LEGACY_RULESYNC_HEADER = "# Generated by rulesync - AI tool configuration files";
|
|
1501
1507
|
const isRulesyncHeader = (line) => {
|
|
1502
1508
|
const trimmed = line.trim();
|
|
1503
1509
|
return trimmed === RULESYNC_HEADER || trimmed === LEGACY_RULESYNC_HEADER;
|
|
1504
1510
|
};
|
|
1511
|
+
const isRulesyncFooter = (line) => {
|
|
1512
|
+
return line.trim() === RULESYNC_FOOTER;
|
|
1513
|
+
};
|
|
1505
1514
|
const isRulesyncEntry = (line) => {
|
|
1506
1515
|
const trimmed = line.trim();
|
|
1507
|
-
if (trimmed === "" || isRulesyncHeader(line)) return false;
|
|
1516
|
+
if (trimmed === "" || isRulesyncHeader(line) || isRulesyncFooter(line)) return false;
|
|
1508
1517
|
return ALL_GITIGNORE_ENTRIES.includes(trimmed);
|
|
1509
1518
|
};
|
|
1519
|
+
const findRulesyncFooterIndex = (lines, start) => {
|
|
1520
|
+
for (let index = start; index < lines.length; index++) {
|
|
1521
|
+
const line = lines[index] ?? "";
|
|
1522
|
+
if (isRulesyncFooter(line)) return index;
|
|
1523
|
+
if (isRulesyncHeader(line)) return -1;
|
|
1524
|
+
}
|
|
1525
|
+
return -1;
|
|
1526
|
+
};
|
|
1527
|
+
const skipLegacyRulesyncBlock = (lines, headerIndex) => {
|
|
1528
|
+
let index = headerIndex + 1;
|
|
1529
|
+
let consecutiveEmptyLines = 0;
|
|
1530
|
+
while (index < lines.length) {
|
|
1531
|
+
const line = lines[index] ?? "";
|
|
1532
|
+
if (line.trim() === "") {
|
|
1533
|
+
consecutiveEmptyLines++;
|
|
1534
|
+
index++;
|
|
1535
|
+
if (consecutiveEmptyLines >= 2) break;
|
|
1536
|
+
continue;
|
|
1537
|
+
}
|
|
1538
|
+
if (isRulesyncEntry(line)) {
|
|
1539
|
+
consecutiveEmptyLines = 0;
|
|
1540
|
+
index++;
|
|
1541
|
+
continue;
|
|
1542
|
+
}
|
|
1543
|
+
break;
|
|
1544
|
+
}
|
|
1545
|
+
return index;
|
|
1546
|
+
};
|
|
1510
1547
|
const removeExistingRulesyncEntries = (content) => {
|
|
1511
1548
|
const lines = content.split("\n");
|
|
1512
1549
|
const filteredLines = [];
|
|
1513
|
-
let
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
const trimmed = line.trim();
|
|
1550
|
+
let index = 0;
|
|
1551
|
+
while (index < lines.length) {
|
|
1552
|
+
const line = lines[index] ?? "";
|
|
1517
1553
|
if (isRulesyncHeader(line)) {
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
if (inRulesyncBlock) {
|
|
1522
|
-
if (trimmed === "") {
|
|
1523
|
-
consecutiveEmptyLines++;
|
|
1524
|
-
if (consecutiveEmptyLines >= 2) {
|
|
1525
|
-
inRulesyncBlock = false;
|
|
1526
|
-
consecutiveEmptyLines = 0;
|
|
1527
|
-
}
|
|
1554
|
+
const footerIndex = findRulesyncFooterIndex(lines, index + 1);
|
|
1555
|
+
if (footerIndex !== -1) {
|
|
1556
|
+
index = footerIndex + 1;
|
|
1528
1557
|
continue;
|
|
1529
1558
|
}
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1559
|
+
index = skipLegacyRulesyncBlock(lines, index);
|
|
1560
|
+
continue;
|
|
1561
|
+
}
|
|
1562
|
+
if (isRulesyncEntry(line)) {
|
|
1563
|
+
index++;
|
|
1564
|
+
continue;
|
|
1536
1565
|
}
|
|
1537
|
-
if (isRulesyncEntry(line)) continue;
|
|
1538
1566
|
filteredLines.push(line);
|
|
1567
|
+
index++;
|
|
1539
1568
|
}
|
|
1540
1569
|
let result = filteredLines.join("\n");
|
|
1541
1570
|
while (result.endsWith("\n\n")) result = result.slice(0, -1);
|
|
@@ -1577,10 +1606,14 @@ const gitignoreCommand = async (logger, options) => {
|
|
|
1577
1606
|
let content = "";
|
|
1578
1607
|
if (await fileExists(filePath)) content = await readFileContent(filePath);
|
|
1579
1608
|
const cleanedContent = removeExistingRulesyncEntries(content);
|
|
1580
|
-
const existingEntries = new Set(content.split("\n").map((line) => line.trim()).filter((line) => line !== "" && !isRulesyncHeader(line)));
|
|
1609
|
+
const existingEntries = new Set(content.split("\n").map((line) => line.trim()).filter((line) => line !== "" && !isRulesyncHeader(line) && !isRulesyncFooter(line)));
|
|
1581
1610
|
const alreadyExistedEntries = entries.filter((entry) => existingEntries.has(entry));
|
|
1582
1611
|
const entriesToAdd = entries.filter((entry) => !existingEntries.has(entry));
|
|
1583
|
-
const rulesyncBlock = [
|
|
1612
|
+
const rulesyncBlock = [
|
|
1613
|
+
RULESYNC_HEADER,
|
|
1614
|
+
...entries,
|
|
1615
|
+
RULESYNC_FOOTER
|
|
1616
|
+
].join("\n");
|
|
1584
1617
|
const newContent = entries.length === 0 ? cleanedContent.trim() ? `${cleanedContent.trimEnd()}\n` : "" : cleanedContent.trim() ? `${cleanedContent.trimEnd()}\n\n${rulesyncBlock}\n` : `${rulesyncBlock}\n`;
|
|
1585
1618
|
if (content === newContent) return {
|
|
1586
1619
|
updated: false,
|
|
@@ -1713,7 +1746,8 @@ async function createConfigFile() {
|
|
|
1713
1746
|
"commands",
|
|
1714
1747
|
"subagents",
|
|
1715
1748
|
"skills",
|
|
1716
|
-
"hooks"
|
|
1749
|
+
"hooks",
|
|
1750
|
+
"permissions"
|
|
1717
1751
|
],
|
|
1718
1752
|
outputRoots: ["."],
|
|
1719
1753
|
delete: true,
|
|
@@ -1857,6 +1891,25 @@ Keep the summary concise and ready to reuse in future tasks.`
|
|
|
1857
1891
|
]
|
|
1858
1892
|
}
|
|
1859
1893
|
}
|
|
1894
|
+
` };
|
|
1895
|
+
const samplePermissionsFile = { content: `{
|
|
1896
|
+
"$schema": "${RULESYNC_PERMISSIONS_SCHEMA_URL}",
|
|
1897
|
+
"permission": {
|
|
1898
|
+
"bash": {
|
|
1899
|
+
"git status": "allow",
|
|
1900
|
+
"git diff": "allow",
|
|
1901
|
+
"ls *": "allow",
|
|
1902
|
+
"rm -rf *": "deny",
|
|
1903
|
+
"*": "ask"
|
|
1904
|
+
},
|
|
1905
|
+
"edit": {
|
|
1906
|
+
"src/**": "allow"
|
|
1907
|
+
},
|
|
1908
|
+
"read": {
|
|
1909
|
+
".env": "deny"
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1860
1913
|
` };
|
|
1861
1914
|
const rulePaths = RulesyncRule.getSettablePaths();
|
|
1862
1915
|
const mcpPaths = RulesyncMcp.getSettablePaths();
|
|
@@ -1865,6 +1918,7 @@ Keep the summary concise and ready to reuse in future tasks.`
|
|
|
1865
1918
|
const skillPaths = RulesyncSkill.getSettablePaths();
|
|
1866
1919
|
const ignorePaths = RulesyncIgnore.getSettablePaths();
|
|
1867
1920
|
const hooksPaths = RulesyncHooks.getSettablePaths();
|
|
1921
|
+
const permissionsPaths = RulesyncPermissions.getSettablePaths();
|
|
1868
1922
|
await ensureDir(rulePaths.recommended.relativeDirPath);
|
|
1869
1923
|
await ensureDir(mcpPaths.recommended.relativeDirPath);
|
|
1870
1924
|
await ensureDir(commandPaths.relativeDirPath);
|
|
@@ -1887,6 +1941,8 @@ Keep the summary concise and ready to reuse in future tasks.`
|
|
|
1887
1941
|
results.push(await writeIfNotExists(ignoreFilepath, sampleIgnoreFile.content));
|
|
1888
1942
|
const hooksFilepath = join(hooksPaths.relativeDirPath, hooksPaths.relativeFilePath);
|
|
1889
1943
|
results.push(await writeIfNotExists(hooksFilepath, sampleHooksFile.content));
|
|
1944
|
+
const permissionsFilepath = join(permissionsPaths.relativeDirPath, permissionsPaths.relativeFilePath);
|
|
1945
|
+
results.push(await writeIfNotExists(permissionsFilepath, samplePermissionsFile.content));
|
|
1890
1946
|
return results;
|
|
1891
1947
|
}
|
|
1892
1948
|
async function writeIfNotExists(path, content) {
|
|
@@ -1928,7 +1984,7 @@ async function initCommand(logger) {
|
|
|
1928
1984
|
}
|
|
1929
1985
|
logger.success("rulesync initialized successfully!");
|
|
1930
1986
|
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}`);
|
|
1987
|
+
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
1988
|
logger.info("2. Run 'rulesync generate' to create configuration files");
|
|
1933
1989
|
}
|
|
1934
1990
|
//#endregion
|
|
@@ -4738,6 +4794,8 @@ async function putHooksFile({ content }) {
|
|
|
4738
4794
|
try {
|
|
4739
4795
|
const outputRoot = process.cwd();
|
|
4740
4796
|
const paths = RulesyncHooks.getSettablePaths();
|
|
4797
|
+
const jsoncRelativePath = join(paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath);
|
|
4798
|
+
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
4799
|
const relativeDirPath = paths.relativeDirPath;
|
|
4742
4800
|
const relativeFilePath = paths.relativeFilePath;
|
|
4743
4801
|
const fullPath = join(outputRoot, relativeDirPath, relativeFilePath);
|
|
@@ -4766,6 +4824,7 @@ async function deleteHooksFile() {
|
|
|
4766
4824
|
const outputRoot = process.cwd();
|
|
4767
4825
|
const paths = RulesyncHooks.getSettablePaths();
|
|
4768
4826
|
await removeFile(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath));
|
|
4827
|
+
await removeFile(join(outputRoot, paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath));
|
|
4769
4828
|
return { relativePathFromCwd: join(paths.relativeDirPath, paths.relativeFilePath) };
|
|
4770
4829
|
} catch (error) {
|
|
4771
4830
|
throw new Error(`Failed to delete hooks file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${formatError(error)}`, { cause: error });
|
|
@@ -5016,6 +5075,8 @@ async function putMcpFile({ content }) {
|
|
|
5016
5075
|
try {
|
|
5017
5076
|
const outputRoot = process.cwd();
|
|
5018
5077
|
const paths = RulesyncMcp.getSettablePaths();
|
|
5078
|
+
const jsoncRelativePath = join(paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath);
|
|
5079
|
+
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
5080
|
const relativeDirPath = paths.recommended.relativeDirPath;
|
|
5020
5081
|
const relativeFilePath = paths.recommended.relativeFilePath;
|
|
5021
5082
|
const fullPath = join(outputRoot, relativeDirPath, relativeFilePath);
|
|
@@ -5046,6 +5107,7 @@ async function deleteMcpFile() {
|
|
|
5046
5107
|
const recommendedPath = join(outputRoot, paths.recommended.relativeDirPath, paths.recommended.relativeFilePath);
|
|
5047
5108
|
const legacyPath = join(outputRoot, paths.legacy.relativeDirPath, paths.legacy.relativeFilePath);
|
|
5048
5109
|
await removeFile(recommendedPath);
|
|
5110
|
+
await removeFile(join(outputRoot, paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath));
|
|
5049
5111
|
await removeFile(legacyPath);
|
|
5050
5112
|
return { relativePathFromCwd: join(paths.recommended.relativeDirPath, paths.recommended.relativeFilePath) };
|
|
5051
5113
|
} catch (error) {
|
|
@@ -5122,6 +5184,8 @@ async function putPermissionsFile({ content }) {
|
|
|
5122
5184
|
try {
|
|
5123
5185
|
const outputRoot = process.cwd();
|
|
5124
5186
|
const paths = RulesyncPermissions.getSettablePaths();
|
|
5187
|
+
const jsoncRelativePath = join(paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath);
|
|
5188
|
+
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
5189
|
const relativeDirPath = paths.relativeDirPath;
|
|
5126
5190
|
const relativeFilePath = paths.relativeFilePath;
|
|
5127
5191
|
const fullPath = join(outputRoot, relativeDirPath, relativeFilePath);
|
|
@@ -5150,6 +5214,7 @@ async function deletePermissionsFile() {
|
|
|
5150
5214
|
const outputRoot = process.cwd();
|
|
5151
5215
|
const paths = RulesyncPermissions.getSettablePaths();
|
|
5152
5216
|
await removeFile(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath));
|
|
5217
|
+
await removeFile(join(outputRoot, paths.jsonc.relativeDirPath, paths.jsonc.relativeFilePath));
|
|
5153
5218
|
return { relativePathFromCwd: join(paths.relativeDirPath, paths.relativeFilePath) };
|
|
5154
5219
|
} catch (error) {
|
|
5155
5220
|
throw new Error(`Failed to delete permissions file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}): ${formatError(error)}`, { cause: error });
|
|
@@ -6448,7 +6513,7 @@ function wrapCommand$1({ name, errorCode, handler, getVersion, loggerFactory = c
|
|
|
6448
6513
|
}
|
|
6449
6514
|
//#endregion
|
|
6450
6515
|
//#region src/cli/index.ts
|
|
6451
|
-
const getVersion = () => "
|
|
6516
|
+
const getVersion = () => "10.0.0";
|
|
6452
6517
|
function wrapCommand(name, errorCode, handler) {
|
|
6453
6518
|
return wrapCommand$1({
|
|
6454
6519
|
name,
|