rulesync 6.6.0 → 6.6.2
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 +10 -4
- package/dist/index.cjs +163 -96
- package/dist/index.js +163 -96
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -120,6 +120,9 @@ Get-FileHash rulesync.exe -Algorithm SHA256 | ForEach-Object {
|
|
|
120
120
|
```bash
|
|
121
121
|
# Create necessary directories, sample rule files, and configuration file
|
|
122
122
|
npx rulesync init
|
|
123
|
+
|
|
124
|
+
# Install official skills (recommended)
|
|
125
|
+
npx rulesync fetch dyoshikawa/rulesync --features skills
|
|
123
126
|
```
|
|
124
127
|
|
|
125
128
|
On the other hand, if you already have AI tool configurations:
|
|
@@ -231,7 +234,7 @@ npx rulesync generate --targets "*" --features rules
|
|
|
231
234
|
# Generate simulated commands and subagents
|
|
232
235
|
npx rulesync generate --targets copilot,cursor,codexcli --features commands,subagents --simulate-commands --simulate-subagents
|
|
233
236
|
|
|
234
|
-
#
|
|
237
|
+
# Dry run: show changes without writing files
|
|
235
238
|
npx rulesync generate --dry-run --targets claudecode --features rules
|
|
236
239
|
|
|
237
240
|
# Check if files are up to date (for CI/CD pipelines)
|
|
@@ -250,13 +253,13 @@ npx rulesync update --check
|
|
|
250
253
|
npx rulesync update --force
|
|
251
254
|
```
|
|
252
255
|
|
|
253
|
-
##
|
|
256
|
+
## Dry Run
|
|
254
257
|
|
|
255
|
-
Rulesync provides two
|
|
258
|
+
Rulesync provides two dry run options for the `generate` command that allow you to see what changes would be made without actually writing files:
|
|
256
259
|
|
|
257
260
|
### `--dry-run`
|
|
258
261
|
|
|
259
|
-
|
|
262
|
+
Show what would be written or deleted without actually writing any files. Changes are displayed with a `[DRY RUN]` prefix.
|
|
260
263
|
|
|
261
264
|
```bash
|
|
262
265
|
npx rulesync generate --dry-run --targets claudecode --features rules
|
|
@@ -333,6 +336,9 @@ npx rulesync fetch owner/repo@v1.0.0 --features rules,commands
|
|
|
333
336
|
export GITHUB_TOKEN=ghp_xxxx
|
|
334
337
|
npx rulesync fetch owner/private-repo
|
|
335
338
|
|
|
339
|
+
# Or use GitHub CLI to get the token
|
|
340
|
+
GITHUB_TOKEN=$(gh auth token) npx rulesync fetch owner/private-repo
|
|
341
|
+
|
|
336
342
|
# Preserve existing files (skip conflicts)
|
|
337
343
|
npx rulesync fetch owner/repo --conflict skip
|
|
338
344
|
|
package/dist/index.cjs
CHANGED
|
@@ -125,6 +125,7 @@ var Logger = class {
|
|
|
125
125
|
var logger = new Logger();
|
|
126
126
|
|
|
127
127
|
// src/lib/fetch.ts
|
|
128
|
+
var import_promise = require("es-toolkit/promise");
|
|
128
129
|
var import_node_path107 = require("path");
|
|
129
130
|
|
|
130
131
|
// src/constants/rulesync-paths.ts
|
|
@@ -10409,6 +10410,9 @@ var ToolRule = class extends ToolFile {
|
|
|
10409
10410
|
return false;
|
|
10410
10411
|
}
|
|
10411
10412
|
};
|
|
10413
|
+
function buildToolPath(toolDir, subDir, excludeToolDir) {
|
|
10414
|
+
return excludeToolDir ? subDir : (0, import_node_path84.join)(toolDir, subDir);
|
|
10415
|
+
}
|
|
10412
10416
|
|
|
10413
10417
|
// src/features/rules/agentsmd-rule.ts
|
|
10414
10418
|
var AgentsMdRule = class _AgentsMdRule extends ToolRule {
|
|
@@ -10426,7 +10430,7 @@ var AgentsMdRule = class _AgentsMdRule extends ToolRule {
|
|
|
10426
10430
|
relativeFilePath: "AGENTS.md"
|
|
10427
10431
|
},
|
|
10428
10432
|
nonRoot: {
|
|
10429
|
-
relativeDirPath:
|
|
10433
|
+
relativeDirPath: buildToolPath(".agents", "memories", _options.excludeToolDir)
|
|
10430
10434
|
}
|
|
10431
10435
|
};
|
|
10432
10436
|
}
|
|
@@ -10666,7 +10670,7 @@ var AntigravityRule = class _AntigravityRule extends ToolRule {
|
|
|
10666
10670
|
static getSettablePaths(_options = {}) {
|
|
10667
10671
|
return {
|
|
10668
10672
|
nonRoot: {
|
|
10669
|
-
relativeDirPath:
|
|
10673
|
+
relativeDirPath: buildToolPath(".agent", "rules", _options.excludeToolDir)
|
|
10670
10674
|
}
|
|
10671
10675
|
};
|
|
10672
10676
|
}
|
|
@@ -10842,7 +10846,7 @@ var AugmentcodeLegacyRule = class _AugmentcodeLegacyRule extends ToolRule {
|
|
|
10842
10846
|
relativeFilePath: ".augment-guidelines"
|
|
10843
10847
|
},
|
|
10844
10848
|
nonRoot: {
|
|
10845
|
-
relativeDirPath:
|
|
10849
|
+
relativeDirPath: buildToolPath(".augment", "rules", _options.excludeToolDir)
|
|
10846
10850
|
}
|
|
10847
10851
|
};
|
|
10848
10852
|
}
|
|
@@ -10915,7 +10919,7 @@ var AugmentcodeRule = class _AugmentcodeRule extends ToolRule {
|
|
|
10915
10919
|
static getSettablePaths(_options = {}) {
|
|
10916
10920
|
return {
|
|
10917
10921
|
nonRoot: {
|
|
10918
|
-
relativeDirPath:
|
|
10922
|
+
relativeDirPath: buildToolPath(".augment", "rules", _options.excludeToolDir)
|
|
10919
10923
|
}
|
|
10920
10924
|
};
|
|
10921
10925
|
}
|
|
@@ -10984,7 +10988,7 @@ var ClaudecodeLegacyRule = class _ClaudecodeLegacyRule extends ToolRule {
|
|
|
10984
10988
|
if (global) {
|
|
10985
10989
|
return {
|
|
10986
10990
|
root: {
|
|
10987
|
-
relativeDirPath:
|
|
10991
|
+
relativeDirPath: buildToolPath(".claude", ".", excludeToolDir),
|
|
10988
10992
|
relativeFilePath: "CLAUDE.md"
|
|
10989
10993
|
}
|
|
10990
10994
|
};
|
|
@@ -10995,7 +10999,7 @@ var ClaudecodeLegacyRule = class _ClaudecodeLegacyRule extends ToolRule {
|
|
|
10995
10999
|
relativeFilePath: "CLAUDE.md"
|
|
10996
11000
|
},
|
|
10997
11001
|
nonRoot: {
|
|
10998
|
-
relativeDirPath:
|
|
11002
|
+
relativeDirPath: buildToolPath(".claude", "memories", excludeToolDir)
|
|
10999
11003
|
}
|
|
11000
11004
|
};
|
|
11001
11005
|
}
|
|
@@ -11099,7 +11103,7 @@ var ClaudecodeRule = class _ClaudecodeRule extends ToolRule {
|
|
|
11099
11103
|
if (global) {
|
|
11100
11104
|
return {
|
|
11101
11105
|
root: {
|
|
11102
|
-
relativeDirPath:
|
|
11106
|
+
relativeDirPath: buildToolPath(".claude", ".", excludeToolDir),
|
|
11103
11107
|
relativeFilePath: "CLAUDE.md"
|
|
11104
11108
|
}
|
|
11105
11109
|
};
|
|
@@ -11110,7 +11114,7 @@ var ClaudecodeRule = class _ClaudecodeRule extends ToolRule {
|
|
|
11110
11114
|
relativeFilePath: "CLAUDE.md"
|
|
11111
11115
|
},
|
|
11112
11116
|
nonRoot: {
|
|
11113
|
-
relativeDirPath:
|
|
11117
|
+
relativeDirPath: buildToolPath(".claude", "rules", excludeToolDir)
|
|
11114
11118
|
}
|
|
11115
11119
|
};
|
|
11116
11120
|
}
|
|
@@ -11376,7 +11380,7 @@ var CodexcliRule = class _CodexcliRule extends ToolRule {
|
|
|
11376
11380
|
if (global) {
|
|
11377
11381
|
return {
|
|
11378
11382
|
root: {
|
|
11379
|
-
relativeDirPath:
|
|
11383
|
+
relativeDirPath: buildToolPath(".codex", ".", excludeToolDir),
|
|
11380
11384
|
relativeFilePath: "AGENTS.md"
|
|
11381
11385
|
}
|
|
11382
11386
|
};
|
|
@@ -11387,7 +11391,7 @@ var CodexcliRule = class _CodexcliRule extends ToolRule {
|
|
|
11387
11391
|
relativeFilePath: "AGENTS.md"
|
|
11388
11392
|
},
|
|
11389
11393
|
nonRoot: {
|
|
11390
|
-
relativeDirPath:
|
|
11394
|
+
relativeDirPath: buildToolPath(".codex", "memories", excludeToolDir)
|
|
11391
11395
|
}
|
|
11392
11396
|
};
|
|
11393
11397
|
}
|
|
@@ -11489,11 +11493,11 @@ var CopilotRule = class _CopilotRule extends ToolRule {
|
|
|
11489
11493
|
static getSettablePaths(_options = {}) {
|
|
11490
11494
|
return {
|
|
11491
11495
|
root: {
|
|
11492
|
-
relativeDirPath:
|
|
11496
|
+
relativeDirPath: buildToolPath(".github", ".", _options.excludeToolDir),
|
|
11493
11497
|
relativeFilePath: "copilot-instructions.md"
|
|
11494
11498
|
},
|
|
11495
11499
|
nonRoot: {
|
|
11496
|
-
relativeDirPath:
|
|
11500
|
+
relativeDirPath: buildToolPath(".github", "instructions", _options.excludeToolDir)
|
|
11497
11501
|
}
|
|
11498
11502
|
};
|
|
11499
11503
|
}
|
|
@@ -11677,7 +11681,7 @@ var CursorRule = class _CursorRule extends ToolRule {
|
|
|
11677
11681
|
static getSettablePaths(_options = {}) {
|
|
11678
11682
|
return {
|
|
11679
11683
|
nonRoot: {
|
|
11680
|
-
relativeDirPath:
|
|
11684
|
+
relativeDirPath: buildToolPath(".cursor", "rules", _options.excludeToolDir)
|
|
11681
11685
|
}
|
|
11682
11686
|
};
|
|
11683
11687
|
}
|
|
@@ -11879,21 +11883,21 @@ var FactorydroidRule = class _FactorydroidRule extends ToolRule {
|
|
|
11879
11883
|
if (options?.global) {
|
|
11880
11884
|
return {
|
|
11881
11885
|
root: {
|
|
11882
|
-
relativeDirPath:
|
|
11886
|
+
relativeDirPath: buildToolPath(".factorydroid", ".", options.excludeToolDir),
|
|
11883
11887
|
relativeFilePath: "AGENTS.md"
|
|
11884
11888
|
},
|
|
11885
11889
|
nonRoot: {
|
|
11886
|
-
relativeDirPath:
|
|
11890
|
+
relativeDirPath: buildToolPath(".factorydroid", "memories", options.excludeToolDir)
|
|
11887
11891
|
}
|
|
11888
11892
|
};
|
|
11889
11893
|
}
|
|
11890
11894
|
return {
|
|
11891
11895
|
root: {
|
|
11892
|
-
relativeDirPath:
|
|
11896
|
+
relativeDirPath: buildToolPath(".factorydroid", ".", options?.excludeToolDir),
|
|
11893
11897
|
relativeFilePath: "AGENTS.md"
|
|
11894
11898
|
},
|
|
11895
11899
|
nonRoot: {
|
|
11896
|
-
relativeDirPath:
|
|
11900
|
+
relativeDirPath: buildToolPath(".factorydroid", "memories", options?.excludeToolDir)
|
|
11897
11901
|
}
|
|
11898
11902
|
};
|
|
11899
11903
|
}
|
|
@@ -11974,7 +11978,7 @@ var GeminiCliRule = class _GeminiCliRule extends ToolRule {
|
|
|
11974
11978
|
if (global) {
|
|
11975
11979
|
return {
|
|
11976
11980
|
root: {
|
|
11977
|
-
relativeDirPath:
|
|
11981
|
+
relativeDirPath: buildToolPath(".gemini", ".", excludeToolDir),
|
|
11978
11982
|
relativeFilePath: "GEMINI.md"
|
|
11979
11983
|
}
|
|
11980
11984
|
};
|
|
@@ -11985,7 +11989,7 @@ var GeminiCliRule = class _GeminiCliRule extends ToolRule {
|
|
|
11985
11989
|
relativeFilePath: "GEMINI.md"
|
|
11986
11990
|
},
|
|
11987
11991
|
nonRoot: {
|
|
11988
|
-
relativeDirPath:
|
|
11992
|
+
relativeDirPath: buildToolPath(".gemini", "memories", excludeToolDir)
|
|
11989
11993
|
}
|
|
11990
11994
|
};
|
|
11991
11995
|
}
|
|
@@ -12079,11 +12083,11 @@ var JunieRule = class _JunieRule extends ToolRule {
|
|
|
12079
12083
|
static getSettablePaths(_options = {}) {
|
|
12080
12084
|
return {
|
|
12081
12085
|
root: {
|
|
12082
|
-
relativeDirPath:
|
|
12086
|
+
relativeDirPath: buildToolPath(".junie", ".", _options.excludeToolDir),
|
|
12083
12087
|
relativeFilePath: "guidelines.md"
|
|
12084
12088
|
},
|
|
12085
12089
|
nonRoot: {
|
|
12086
|
-
relativeDirPath:
|
|
12090
|
+
relativeDirPath: buildToolPath(".junie", "memories", _options.excludeToolDir)
|
|
12087
12091
|
}
|
|
12088
12092
|
};
|
|
12089
12093
|
}
|
|
@@ -12154,7 +12158,7 @@ var KiloRule = class _KiloRule extends ToolRule {
|
|
|
12154
12158
|
static getSettablePaths(_options = {}) {
|
|
12155
12159
|
return {
|
|
12156
12160
|
nonRoot: {
|
|
12157
|
-
relativeDirPath:
|
|
12161
|
+
relativeDirPath: buildToolPath(".kilocode", "rules", _options.excludeToolDir)
|
|
12158
12162
|
}
|
|
12159
12163
|
};
|
|
12160
12164
|
}
|
|
@@ -12221,7 +12225,7 @@ var KiroRule = class _KiroRule extends ToolRule {
|
|
|
12221
12225
|
static getSettablePaths(_options = {}) {
|
|
12222
12226
|
return {
|
|
12223
12227
|
nonRoot: {
|
|
12224
|
-
relativeDirPath:
|
|
12228
|
+
relativeDirPath: buildToolPath(".kiro", "steering", _options.excludeToolDir)
|
|
12225
12229
|
}
|
|
12226
12230
|
};
|
|
12227
12231
|
}
|
|
@@ -12294,7 +12298,7 @@ var OpenCodeRule = class _OpenCodeRule extends ToolRule {
|
|
|
12294
12298
|
relativeFilePath: "AGENTS.md"
|
|
12295
12299
|
},
|
|
12296
12300
|
nonRoot: {
|
|
12297
|
-
relativeDirPath:
|
|
12301
|
+
relativeDirPath: buildToolPath(".opencode", "memories", _options.excludeToolDir)
|
|
12298
12302
|
}
|
|
12299
12303
|
};
|
|
12300
12304
|
}
|
|
@@ -12369,7 +12373,7 @@ var QwencodeRule = class _QwencodeRule extends ToolRule {
|
|
|
12369
12373
|
relativeFilePath: "QWEN.md"
|
|
12370
12374
|
},
|
|
12371
12375
|
nonRoot: {
|
|
12372
|
-
relativeDirPath:
|
|
12376
|
+
relativeDirPath: buildToolPath(".qwen", "memories", _options.excludeToolDir)
|
|
12373
12377
|
}
|
|
12374
12378
|
};
|
|
12375
12379
|
}
|
|
@@ -12525,7 +12529,7 @@ var RooRule = class _RooRule extends ToolRule {
|
|
|
12525
12529
|
static getSettablePaths(_options = {}) {
|
|
12526
12530
|
return {
|
|
12527
12531
|
nonRoot: {
|
|
12528
|
-
relativeDirPath:
|
|
12532
|
+
relativeDirPath: buildToolPath(".roo", "rules", _options.excludeToolDir)
|
|
12529
12533
|
}
|
|
12530
12534
|
};
|
|
12531
12535
|
}
|
|
@@ -12620,7 +12624,7 @@ var WarpRule = class _WarpRule extends ToolRule {
|
|
|
12620
12624
|
relativeFilePath: "WARP.md"
|
|
12621
12625
|
},
|
|
12622
12626
|
nonRoot: {
|
|
12623
|
-
relativeDirPath:
|
|
12627
|
+
relativeDirPath: buildToolPath(".warp", "memories", _options.excludeToolDir)
|
|
12624
12628
|
}
|
|
12625
12629
|
};
|
|
12626
12630
|
}
|
|
@@ -12691,7 +12695,7 @@ var WindsurfRule = class _WindsurfRule extends ToolRule {
|
|
|
12691
12695
|
static getSettablePaths(_options = {}) {
|
|
12692
12696
|
return {
|
|
12693
12697
|
nonRoot: {
|
|
12694
|
-
relativeDirPath:
|
|
12698
|
+
relativeDirPath: buildToolPath(".windsurf", "rules", _options.excludeToolDir)
|
|
12695
12699
|
}
|
|
12696
12700
|
};
|
|
12697
12701
|
}
|
|
@@ -13543,6 +13547,17 @@ var GitHubClientError = class extends Error {
|
|
|
13543
13547
|
this.name = "GitHubClientError";
|
|
13544
13548
|
}
|
|
13545
13549
|
};
|
|
13550
|
+
function logGitHubAuthHints(error) {
|
|
13551
|
+
logger.error(`GitHub API Error: ${error.message}`);
|
|
13552
|
+
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
13553
|
+
logger.info(
|
|
13554
|
+
"Tip: Set GITHUB_TOKEN or GH_TOKEN environment variable for private repositories or better rate limits."
|
|
13555
|
+
);
|
|
13556
|
+
logger.info(
|
|
13557
|
+
"Tip: If you use GitHub CLI, you can use `GITHUB_TOKEN=$(gh auth token) rulesync fetch ...`"
|
|
13558
|
+
);
|
|
13559
|
+
}
|
|
13560
|
+
}
|
|
13546
13561
|
var GitHubClient = class {
|
|
13547
13562
|
octokit;
|
|
13548
13563
|
hasToken;
|
|
@@ -13857,13 +13872,25 @@ function parseSource(source) {
|
|
|
13857
13872
|
}
|
|
13858
13873
|
return { provider: "github", ...parseShorthand(source) };
|
|
13859
13874
|
}
|
|
13875
|
+
var GITHUB_HOSTS = /* @__PURE__ */ new Set(["github.com", "www.github.com"]);
|
|
13876
|
+
var GITLAB_HOSTS = /* @__PURE__ */ new Set(["gitlab.com", "www.gitlab.com"]);
|
|
13877
|
+
var MAX_RECURSION_DEPTH = 15;
|
|
13878
|
+
var FETCH_CONCURRENCY_LIMIT = 10;
|
|
13879
|
+
async function withSemaphore(semaphore, fn) {
|
|
13880
|
+
await semaphore.acquire();
|
|
13881
|
+
try {
|
|
13882
|
+
return await fn();
|
|
13883
|
+
} finally {
|
|
13884
|
+
semaphore.release();
|
|
13885
|
+
}
|
|
13886
|
+
}
|
|
13860
13887
|
function parseUrl(url) {
|
|
13861
13888
|
const urlObj = new URL(url);
|
|
13862
13889
|
const host = urlObj.hostname.toLowerCase();
|
|
13863
13890
|
let provider;
|
|
13864
|
-
if (
|
|
13891
|
+
if (GITHUB_HOSTS.has(host)) {
|
|
13865
13892
|
provider = "github";
|
|
13866
|
-
} else if (
|
|
13893
|
+
} else if (GITLAB_HOSTS.has(host)) {
|
|
13867
13894
|
provider = "gitlab";
|
|
13868
13895
|
} else {
|
|
13869
13896
|
throw new Error(
|
|
@@ -13996,13 +14023,15 @@ async function fetchFiles(params) {
|
|
|
13996
14023
|
conflictStrategy
|
|
13997
14024
|
});
|
|
13998
14025
|
}
|
|
14026
|
+
const semaphore = new import_promise.Semaphore(FETCH_CONCURRENCY_LIMIT);
|
|
13999
14027
|
const filesToFetch = await collectFeatureFiles({
|
|
14000
14028
|
client,
|
|
14001
14029
|
owner: parsed.owner,
|
|
14002
14030
|
repo: parsed.repo,
|
|
14003
14031
|
basePath: resolvedPath,
|
|
14004
14032
|
ref,
|
|
14005
|
-
enabledFeatures
|
|
14033
|
+
enabledFeatures,
|
|
14034
|
+
semaphore
|
|
14006
14035
|
});
|
|
14007
14036
|
if (filesToFetch.length === 0) {
|
|
14008
14037
|
logger.warn(`No files found matching enabled features: ${enabledFeatures.join(", ")}`);
|
|
@@ -14015,9 +14044,8 @@ async function fetchFiles(params) {
|
|
|
14015
14044
|
skipped: 0
|
|
14016
14045
|
};
|
|
14017
14046
|
}
|
|
14018
|
-
const results = [];
|
|
14019
14047
|
const outputBasePath = (0, import_node_path107.join)(baseDir, outputDir);
|
|
14020
|
-
for (const {
|
|
14048
|
+
for (const { relativePath, size } of filesToFetch) {
|
|
14021
14049
|
checkPathTraversal({
|
|
14022
14050
|
relativePath,
|
|
14023
14051
|
intendedRootDir: outputBasePath
|
|
@@ -14027,20 +14055,25 @@ async function fetchFiles(params) {
|
|
|
14027
14055
|
`File "${relativePath}" exceeds maximum size limit (${(size / 1024 / 1024).toFixed(2)}MB > ${MAX_FILE_SIZE / 1024 / 1024}MB)`
|
|
14028
14056
|
);
|
|
14029
14057
|
}
|
|
14030
|
-
|
|
14031
|
-
|
|
14032
|
-
|
|
14033
|
-
|
|
14034
|
-
|
|
14035
|
-
|
|
14036
|
-
|
|
14037
|
-
|
|
14058
|
+
}
|
|
14059
|
+
const results = await Promise.all(
|
|
14060
|
+
filesToFetch.map(async ({ remotePath, relativePath }) => {
|
|
14061
|
+
const localPath = (0, import_node_path107.join)(outputBasePath, relativePath);
|
|
14062
|
+
const exists = await fileExists(localPath);
|
|
14063
|
+
if (exists && conflictStrategy === "skip") {
|
|
14064
|
+
logger.debug(`Skipping existing file: ${relativePath}`);
|
|
14065
|
+
return { relativePath, status: "skipped" };
|
|
14066
|
+
}
|
|
14067
|
+
const content = await withSemaphore(
|
|
14068
|
+
semaphore,
|
|
14069
|
+
() => client.getFileContent(parsed.owner, parsed.repo, remotePath, ref)
|
|
14070
|
+
);
|
|
14038
14071
|
await writeFileContent(localPath, content);
|
|
14039
|
-
status = exists ? "overwritten" : "created";
|
|
14072
|
+
const status = exists ? "overwritten" : "created";
|
|
14040
14073
|
logger.debug(`Wrote: ${relativePath} (${status})`);
|
|
14041
|
-
|
|
14042
|
-
|
|
14043
|
-
|
|
14074
|
+
return { relativePath, status };
|
|
14075
|
+
})
|
|
14076
|
+
);
|
|
14044
14077
|
const summary = {
|
|
14045
14078
|
source: `${parsed.owner}/${parsed.repo}`,
|
|
14046
14079
|
ref,
|
|
@@ -14052,24 +14085,29 @@ async function fetchFiles(params) {
|
|
|
14052
14085
|
return summary;
|
|
14053
14086
|
}
|
|
14054
14087
|
async function collectFeatureFiles(params) {
|
|
14055
|
-
const { client, owner, repo, basePath, ref, enabledFeatures } = params;
|
|
14056
|
-
const
|
|
14057
|
-
|
|
14058
|
-
|
|
14059
|
-
|
|
14088
|
+
const { client, owner, repo, basePath, ref, enabledFeatures, semaphore } = params;
|
|
14089
|
+
const tasks = enabledFeatures.flatMap(
|
|
14090
|
+
(feature) => FEATURE_PATHS[feature].map((featurePath) => ({ feature, featurePath }))
|
|
14091
|
+
);
|
|
14092
|
+
const results = await Promise.all(
|
|
14093
|
+
tasks.map(async ({ featurePath }) => {
|
|
14060
14094
|
const fullPath = basePath === "." || basePath === "" ? featurePath : (0, import_node_path107.join)(basePath, featurePath);
|
|
14095
|
+
const collected = [];
|
|
14061
14096
|
try {
|
|
14062
14097
|
if (featurePath.includes(".")) {
|
|
14063
14098
|
try {
|
|
14064
|
-
const entries = await
|
|
14065
|
-
|
|
14066
|
-
|
|
14067
|
-
|
|
14068
|
-
|
|
14099
|
+
const entries = await withSemaphore(
|
|
14100
|
+
semaphore,
|
|
14101
|
+
() => client.listDirectory(
|
|
14102
|
+
owner,
|
|
14103
|
+
repo,
|
|
14104
|
+
basePath === "." || basePath === "" ? "." : basePath,
|
|
14105
|
+
ref
|
|
14106
|
+
)
|
|
14069
14107
|
);
|
|
14070
14108
|
const fileEntry = entries.find((e) => e.name === featurePath && e.type === "file");
|
|
14071
14109
|
if (fileEntry) {
|
|
14072
|
-
|
|
14110
|
+
collected.push({
|
|
14073
14111
|
remotePath: fileEntry.path,
|
|
14074
14112
|
relativePath: featurePath,
|
|
14075
14113
|
size: fileEntry.size
|
|
@@ -14083,10 +14121,17 @@ async function collectFeatureFiles(params) {
|
|
|
14083
14121
|
}
|
|
14084
14122
|
}
|
|
14085
14123
|
} else {
|
|
14086
|
-
const dirFiles = await listDirectoryRecursive(
|
|
14124
|
+
const dirFiles = await listDirectoryRecursive({
|
|
14125
|
+
client,
|
|
14126
|
+
owner,
|
|
14127
|
+
repo,
|
|
14128
|
+
path: fullPath,
|
|
14129
|
+
ref,
|
|
14130
|
+
semaphore
|
|
14131
|
+
});
|
|
14087
14132
|
for (const file of dirFiles) {
|
|
14088
14133
|
const relativePath = basePath === "." || basePath === "" ? file.path : file.path.substring(basePath.length + 1);
|
|
14089
|
-
|
|
14134
|
+
collected.push({
|
|
14090
14135
|
remotePath: file.path,
|
|
14091
14136
|
relativePath,
|
|
14092
14137
|
size: file.size
|
|
@@ -14096,26 +14141,49 @@ async function collectFeatureFiles(params) {
|
|
|
14096
14141
|
} catch (error) {
|
|
14097
14142
|
if (isNotFoundError(error)) {
|
|
14098
14143
|
logger.debug(`Feature not found: ${fullPath}`);
|
|
14099
|
-
|
|
14144
|
+
return collected;
|
|
14100
14145
|
}
|
|
14101
14146
|
throw error;
|
|
14102
14147
|
}
|
|
14103
|
-
|
|
14104
|
-
|
|
14105
|
-
|
|
14148
|
+
return collected;
|
|
14149
|
+
})
|
|
14150
|
+
);
|
|
14151
|
+
return results.flat();
|
|
14106
14152
|
}
|
|
14107
|
-
async function listDirectoryRecursive(
|
|
14108
|
-
const
|
|
14153
|
+
async function listDirectoryRecursive(params) {
|
|
14154
|
+
const { client, owner, repo, path: path4, ref, depth = 0, semaphore } = params;
|
|
14155
|
+
if (depth > MAX_RECURSION_DEPTH) {
|
|
14156
|
+
throw new Error(
|
|
14157
|
+
`Maximum recursion depth (${MAX_RECURSION_DEPTH}) exceeded while listing directory: ${path4}`
|
|
14158
|
+
);
|
|
14159
|
+
}
|
|
14160
|
+
const entries = await withSemaphore(
|
|
14161
|
+
semaphore,
|
|
14162
|
+
() => client.listDirectory(owner, repo, path4, ref)
|
|
14163
|
+
);
|
|
14109
14164
|
const files = [];
|
|
14165
|
+
const directories = [];
|
|
14110
14166
|
for (const entry of entries) {
|
|
14111
14167
|
if (entry.type === "file") {
|
|
14112
14168
|
files.push(entry);
|
|
14113
14169
|
} else if (entry.type === "dir") {
|
|
14114
|
-
|
|
14115
|
-
files.push(...subFiles);
|
|
14170
|
+
directories.push(entry);
|
|
14116
14171
|
}
|
|
14117
14172
|
}
|
|
14118
|
-
|
|
14173
|
+
const subResults = await Promise.all(
|
|
14174
|
+
directories.map(
|
|
14175
|
+
(dir) => listDirectoryRecursive({
|
|
14176
|
+
client,
|
|
14177
|
+
owner,
|
|
14178
|
+
repo,
|
|
14179
|
+
path: dir.path,
|
|
14180
|
+
ref,
|
|
14181
|
+
depth: depth + 1,
|
|
14182
|
+
semaphore
|
|
14183
|
+
})
|
|
14184
|
+
)
|
|
14185
|
+
);
|
|
14186
|
+
return [...files, ...subResults.flat()];
|
|
14119
14187
|
}
|
|
14120
14188
|
async function fetchAndConvertToolFiles(params) {
|
|
14121
14189
|
const {
|
|
@@ -14131,6 +14199,7 @@ async function fetchAndConvertToolFiles(params) {
|
|
|
14131
14199
|
} = params;
|
|
14132
14200
|
const tempDir = await createTempDirectory();
|
|
14133
14201
|
logger.debug(`Created temp directory: ${tempDir}`);
|
|
14202
|
+
const semaphore = new import_promise.Semaphore(FETCH_CONCURRENCY_LIMIT);
|
|
14134
14203
|
try {
|
|
14135
14204
|
const filesToFetch = await collectFeatureFiles({
|
|
14136
14205
|
client,
|
|
@@ -14138,7 +14207,8 @@ async function fetchAndConvertToolFiles(params) {
|
|
|
14138
14207
|
repo: parsed.repo,
|
|
14139
14208
|
basePath: resolvedPath,
|
|
14140
14209
|
ref,
|
|
14141
|
-
enabledFeatures
|
|
14210
|
+
enabledFeatures,
|
|
14211
|
+
semaphore
|
|
14142
14212
|
});
|
|
14143
14213
|
if (filesToFetch.length === 0) {
|
|
14144
14214
|
logger.warn(`No files found matching enabled features: ${enabledFeatures.join(", ")}`);
|
|
@@ -14151,25 +14221,31 @@ async function fetchAndConvertToolFiles(params) {
|
|
|
14151
14221
|
skipped: 0
|
|
14152
14222
|
};
|
|
14153
14223
|
}
|
|
14154
|
-
const
|
|
14155
|
-
const fetchedFiles = [];
|
|
14156
|
-
for (const { remotePath, relativePath, size } of filesToFetch) {
|
|
14224
|
+
for (const { relativePath, size } of filesToFetch) {
|
|
14157
14225
|
if (size > MAX_FILE_SIZE) {
|
|
14158
14226
|
throw new GitHubClientError(
|
|
14159
14227
|
`File "${relativePath}" exceeds maximum size limit (${(size / 1024 / 1024).toFixed(2)}MB > ${MAX_FILE_SIZE / 1024 / 1024}MB)`
|
|
14160
14228
|
);
|
|
14161
14229
|
}
|
|
14162
|
-
const toolRelativePath = mapToToolPath(relativePath, toolPaths);
|
|
14163
|
-
checkPathTraversal({
|
|
14164
|
-
relativePath: toolRelativePath,
|
|
14165
|
-
intendedRootDir: tempDir
|
|
14166
|
-
});
|
|
14167
|
-
const localPath = (0, import_node_path107.join)(tempDir, toolRelativePath);
|
|
14168
|
-
const content = await client.getFileContent(parsed.owner, parsed.repo, remotePath, ref);
|
|
14169
|
-
await writeFileContent(localPath, content);
|
|
14170
|
-
fetchedFiles.push(toolRelativePath);
|
|
14171
|
-
logger.debug(`Fetched to temp: ${toolRelativePath}`);
|
|
14172
14230
|
}
|
|
14231
|
+
const toolPaths = getToolPathMapping(target);
|
|
14232
|
+
await Promise.all(
|
|
14233
|
+
filesToFetch.map(async ({ remotePath, relativePath }) => {
|
|
14234
|
+
const toolRelativePath = mapToToolPath(relativePath, toolPaths);
|
|
14235
|
+
checkPathTraversal({
|
|
14236
|
+
relativePath: toolRelativePath,
|
|
14237
|
+
intendedRootDir: tempDir
|
|
14238
|
+
});
|
|
14239
|
+
const localPath = (0, import_node_path107.join)(tempDir, toolRelativePath);
|
|
14240
|
+
const content = await withSemaphore(
|
|
14241
|
+
semaphore,
|
|
14242
|
+
() => client.getFileContent(parsed.owner, parsed.repo, remotePath, ref)
|
|
14243
|
+
);
|
|
14244
|
+
await writeFileContent(localPath, content);
|
|
14245
|
+
logger.debug(`Fetched to temp: ${toolRelativePath}`);
|
|
14246
|
+
return toolRelativePath;
|
|
14247
|
+
})
|
|
14248
|
+
);
|
|
14173
14249
|
const outputBasePath = (0, import_node_path107.join)(baseDir, outputDir);
|
|
14174
14250
|
const { converted, convertedPaths } = await convertFetchedFilesToRulesync({
|
|
14175
14251
|
tempDir,
|
|
@@ -14307,12 +14383,7 @@ async function fetchCommand(options) {
|
|
|
14307
14383
|
}
|
|
14308
14384
|
} catch (error) {
|
|
14309
14385
|
if (error instanceof GitHubClientError) {
|
|
14310
|
-
|
|
14311
|
-
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
14312
|
-
logger.info(
|
|
14313
|
-
"Tip: Set GITHUB_TOKEN or GH_TOKEN environment variable for private repositories."
|
|
14314
|
-
);
|
|
14315
|
-
}
|
|
14386
|
+
logGitHubAuthHints(error);
|
|
14316
14387
|
} else {
|
|
14317
14388
|
logger.error(formatError(error));
|
|
14318
14389
|
}
|
|
@@ -17183,7 +17254,8 @@ var MAX_DOWNLOAD_SIZE = 500 * 1024 * 1024;
|
|
|
17183
17254
|
var ALLOWED_DOWNLOAD_DOMAINS = [
|
|
17184
17255
|
"github.com",
|
|
17185
17256
|
"objects.githubusercontent.com",
|
|
17186
|
-
"github-releases.githubusercontent.com"
|
|
17257
|
+
"github-releases.githubusercontent.com",
|
|
17258
|
+
"release-assets.githubusercontent.com"
|
|
17187
17259
|
];
|
|
17188
17260
|
var UpdatePermissionError = class extends Error {
|
|
17189
17261
|
constructor(message) {
|
|
@@ -17509,12 +17581,7 @@ async function updateCommand(currentVersion, options) {
|
|
|
17509
17581
|
logger.success(message);
|
|
17510
17582
|
} catch (error) {
|
|
17511
17583
|
if (error instanceof GitHubClientError) {
|
|
17512
|
-
|
|
17513
|
-
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
17514
|
-
logger.info(
|
|
17515
|
-
"Tip: Set GITHUB_TOKEN or GH_TOKEN environment variable for better rate limits."
|
|
17516
|
-
);
|
|
17517
|
-
}
|
|
17584
|
+
logGitHubAuthHints(error);
|
|
17518
17585
|
} else if (error instanceof UpdatePermissionError) {
|
|
17519
17586
|
logger.error(error.message);
|
|
17520
17587
|
logger.info("Tip: Run with elevated privileges (e.g., sudo rulesync update)");
|
|
@@ -17526,7 +17593,7 @@ async function updateCommand(currentVersion, options) {
|
|
|
17526
17593
|
}
|
|
17527
17594
|
|
|
17528
17595
|
// src/cli/index.ts
|
|
17529
|
-
var getVersion = () => "6.6.
|
|
17596
|
+
var getVersion = () => "6.6.2";
|
|
17530
17597
|
var main = async () => {
|
|
17531
17598
|
const program = new import_commander.Command();
|
|
17532
17599
|
const version = getVersion();
|
|
@@ -17626,7 +17693,7 @@ var main = async () => {
|
|
|
17626
17693
|
).option(
|
|
17627
17694
|
"--modular-mcp",
|
|
17628
17695
|
"Generate modular-mcp configuration for context compression (experimental)"
|
|
17629
|
-
).option("--dry-run", "
|
|
17696
|
+
).option("--dry-run", "Dry run: show changes without writing files").option("--check", "Check if files are up to date (exits with code 1 if changes needed)").action(async (options) => {
|
|
17630
17697
|
try {
|
|
17631
17698
|
await generateCommand({
|
|
17632
17699
|
targets: options.targets,
|
package/dist/index.js
CHANGED
|
@@ -102,6 +102,7 @@ var Logger = class {
|
|
|
102
102
|
var logger = new Logger();
|
|
103
103
|
|
|
104
104
|
// src/lib/fetch.ts
|
|
105
|
+
import { Semaphore } from "es-toolkit/promise";
|
|
105
106
|
import { join as join106 } from "path";
|
|
106
107
|
|
|
107
108
|
// src/constants/rulesync-paths.ts
|
|
@@ -10386,6 +10387,9 @@ var ToolRule = class extends ToolFile {
|
|
|
10386
10387
|
return false;
|
|
10387
10388
|
}
|
|
10388
10389
|
};
|
|
10390
|
+
function buildToolPath(toolDir, subDir, excludeToolDir) {
|
|
10391
|
+
return excludeToolDir ? subDir : join83(toolDir, subDir);
|
|
10392
|
+
}
|
|
10389
10393
|
|
|
10390
10394
|
// src/features/rules/agentsmd-rule.ts
|
|
10391
10395
|
var AgentsMdRule = class _AgentsMdRule extends ToolRule {
|
|
@@ -10403,7 +10407,7 @@ var AgentsMdRule = class _AgentsMdRule extends ToolRule {
|
|
|
10403
10407
|
relativeFilePath: "AGENTS.md"
|
|
10404
10408
|
},
|
|
10405
10409
|
nonRoot: {
|
|
10406
|
-
relativeDirPath:
|
|
10410
|
+
relativeDirPath: buildToolPath(".agents", "memories", _options.excludeToolDir)
|
|
10407
10411
|
}
|
|
10408
10412
|
};
|
|
10409
10413
|
}
|
|
@@ -10643,7 +10647,7 @@ var AntigravityRule = class _AntigravityRule extends ToolRule {
|
|
|
10643
10647
|
static getSettablePaths(_options = {}) {
|
|
10644
10648
|
return {
|
|
10645
10649
|
nonRoot: {
|
|
10646
|
-
relativeDirPath:
|
|
10650
|
+
relativeDirPath: buildToolPath(".agent", "rules", _options.excludeToolDir)
|
|
10647
10651
|
}
|
|
10648
10652
|
};
|
|
10649
10653
|
}
|
|
@@ -10819,7 +10823,7 @@ var AugmentcodeLegacyRule = class _AugmentcodeLegacyRule extends ToolRule {
|
|
|
10819
10823
|
relativeFilePath: ".augment-guidelines"
|
|
10820
10824
|
},
|
|
10821
10825
|
nonRoot: {
|
|
10822
|
-
relativeDirPath:
|
|
10826
|
+
relativeDirPath: buildToolPath(".augment", "rules", _options.excludeToolDir)
|
|
10823
10827
|
}
|
|
10824
10828
|
};
|
|
10825
10829
|
}
|
|
@@ -10892,7 +10896,7 @@ var AugmentcodeRule = class _AugmentcodeRule extends ToolRule {
|
|
|
10892
10896
|
static getSettablePaths(_options = {}) {
|
|
10893
10897
|
return {
|
|
10894
10898
|
nonRoot: {
|
|
10895
|
-
relativeDirPath:
|
|
10899
|
+
relativeDirPath: buildToolPath(".augment", "rules", _options.excludeToolDir)
|
|
10896
10900
|
}
|
|
10897
10901
|
};
|
|
10898
10902
|
}
|
|
@@ -10961,7 +10965,7 @@ var ClaudecodeLegacyRule = class _ClaudecodeLegacyRule extends ToolRule {
|
|
|
10961
10965
|
if (global) {
|
|
10962
10966
|
return {
|
|
10963
10967
|
root: {
|
|
10964
|
-
relativeDirPath:
|
|
10968
|
+
relativeDirPath: buildToolPath(".claude", ".", excludeToolDir),
|
|
10965
10969
|
relativeFilePath: "CLAUDE.md"
|
|
10966
10970
|
}
|
|
10967
10971
|
};
|
|
@@ -10972,7 +10976,7 @@ var ClaudecodeLegacyRule = class _ClaudecodeLegacyRule extends ToolRule {
|
|
|
10972
10976
|
relativeFilePath: "CLAUDE.md"
|
|
10973
10977
|
},
|
|
10974
10978
|
nonRoot: {
|
|
10975
|
-
relativeDirPath:
|
|
10979
|
+
relativeDirPath: buildToolPath(".claude", "memories", excludeToolDir)
|
|
10976
10980
|
}
|
|
10977
10981
|
};
|
|
10978
10982
|
}
|
|
@@ -11076,7 +11080,7 @@ var ClaudecodeRule = class _ClaudecodeRule extends ToolRule {
|
|
|
11076
11080
|
if (global) {
|
|
11077
11081
|
return {
|
|
11078
11082
|
root: {
|
|
11079
|
-
relativeDirPath:
|
|
11083
|
+
relativeDirPath: buildToolPath(".claude", ".", excludeToolDir),
|
|
11080
11084
|
relativeFilePath: "CLAUDE.md"
|
|
11081
11085
|
}
|
|
11082
11086
|
};
|
|
@@ -11087,7 +11091,7 @@ var ClaudecodeRule = class _ClaudecodeRule extends ToolRule {
|
|
|
11087
11091
|
relativeFilePath: "CLAUDE.md"
|
|
11088
11092
|
},
|
|
11089
11093
|
nonRoot: {
|
|
11090
|
-
relativeDirPath:
|
|
11094
|
+
relativeDirPath: buildToolPath(".claude", "rules", excludeToolDir)
|
|
11091
11095
|
}
|
|
11092
11096
|
};
|
|
11093
11097
|
}
|
|
@@ -11353,7 +11357,7 @@ var CodexcliRule = class _CodexcliRule extends ToolRule {
|
|
|
11353
11357
|
if (global) {
|
|
11354
11358
|
return {
|
|
11355
11359
|
root: {
|
|
11356
|
-
relativeDirPath:
|
|
11360
|
+
relativeDirPath: buildToolPath(".codex", ".", excludeToolDir),
|
|
11357
11361
|
relativeFilePath: "AGENTS.md"
|
|
11358
11362
|
}
|
|
11359
11363
|
};
|
|
@@ -11364,7 +11368,7 @@ var CodexcliRule = class _CodexcliRule extends ToolRule {
|
|
|
11364
11368
|
relativeFilePath: "AGENTS.md"
|
|
11365
11369
|
},
|
|
11366
11370
|
nonRoot: {
|
|
11367
|
-
relativeDirPath:
|
|
11371
|
+
relativeDirPath: buildToolPath(".codex", "memories", excludeToolDir)
|
|
11368
11372
|
}
|
|
11369
11373
|
};
|
|
11370
11374
|
}
|
|
@@ -11466,11 +11470,11 @@ var CopilotRule = class _CopilotRule extends ToolRule {
|
|
|
11466
11470
|
static getSettablePaths(_options = {}) {
|
|
11467
11471
|
return {
|
|
11468
11472
|
root: {
|
|
11469
|
-
relativeDirPath:
|
|
11473
|
+
relativeDirPath: buildToolPath(".github", ".", _options.excludeToolDir),
|
|
11470
11474
|
relativeFilePath: "copilot-instructions.md"
|
|
11471
11475
|
},
|
|
11472
11476
|
nonRoot: {
|
|
11473
|
-
relativeDirPath:
|
|
11477
|
+
relativeDirPath: buildToolPath(".github", "instructions", _options.excludeToolDir)
|
|
11474
11478
|
}
|
|
11475
11479
|
};
|
|
11476
11480
|
}
|
|
@@ -11654,7 +11658,7 @@ var CursorRule = class _CursorRule extends ToolRule {
|
|
|
11654
11658
|
static getSettablePaths(_options = {}) {
|
|
11655
11659
|
return {
|
|
11656
11660
|
nonRoot: {
|
|
11657
|
-
relativeDirPath:
|
|
11661
|
+
relativeDirPath: buildToolPath(".cursor", "rules", _options.excludeToolDir)
|
|
11658
11662
|
}
|
|
11659
11663
|
};
|
|
11660
11664
|
}
|
|
@@ -11856,21 +11860,21 @@ var FactorydroidRule = class _FactorydroidRule extends ToolRule {
|
|
|
11856
11860
|
if (options?.global) {
|
|
11857
11861
|
return {
|
|
11858
11862
|
root: {
|
|
11859
|
-
relativeDirPath:
|
|
11863
|
+
relativeDirPath: buildToolPath(".factorydroid", ".", options.excludeToolDir),
|
|
11860
11864
|
relativeFilePath: "AGENTS.md"
|
|
11861
11865
|
},
|
|
11862
11866
|
nonRoot: {
|
|
11863
|
-
relativeDirPath:
|
|
11867
|
+
relativeDirPath: buildToolPath(".factorydroid", "memories", options.excludeToolDir)
|
|
11864
11868
|
}
|
|
11865
11869
|
};
|
|
11866
11870
|
}
|
|
11867
11871
|
return {
|
|
11868
11872
|
root: {
|
|
11869
|
-
relativeDirPath:
|
|
11873
|
+
relativeDirPath: buildToolPath(".factorydroid", ".", options?.excludeToolDir),
|
|
11870
11874
|
relativeFilePath: "AGENTS.md"
|
|
11871
11875
|
},
|
|
11872
11876
|
nonRoot: {
|
|
11873
|
-
relativeDirPath:
|
|
11877
|
+
relativeDirPath: buildToolPath(".factorydroid", "memories", options?.excludeToolDir)
|
|
11874
11878
|
}
|
|
11875
11879
|
};
|
|
11876
11880
|
}
|
|
@@ -11951,7 +11955,7 @@ var GeminiCliRule = class _GeminiCliRule extends ToolRule {
|
|
|
11951
11955
|
if (global) {
|
|
11952
11956
|
return {
|
|
11953
11957
|
root: {
|
|
11954
|
-
relativeDirPath:
|
|
11958
|
+
relativeDirPath: buildToolPath(".gemini", ".", excludeToolDir),
|
|
11955
11959
|
relativeFilePath: "GEMINI.md"
|
|
11956
11960
|
}
|
|
11957
11961
|
};
|
|
@@ -11962,7 +11966,7 @@ var GeminiCliRule = class _GeminiCliRule extends ToolRule {
|
|
|
11962
11966
|
relativeFilePath: "GEMINI.md"
|
|
11963
11967
|
},
|
|
11964
11968
|
nonRoot: {
|
|
11965
|
-
relativeDirPath:
|
|
11969
|
+
relativeDirPath: buildToolPath(".gemini", "memories", excludeToolDir)
|
|
11966
11970
|
}
|
|
11967
11971
|
};
|
|
11968
11972
|
}
|
|
@@ -12056,11 +12060,11 @@ var JunieRule = class _JunieRule extends ToolRule {
|
|
|
12056
12060
|
static getSettablePaths(_options = {}) {
|
|
12057
12061
|
return {
|
|
12058
12062
|
root: {
|
|
12059
|
-
relativeDirPath:
|
|
12063
|
+
relativeDirPath: buildToolPath(".junie", ".", _options.excludeToolDir),
|
|
12060
12064
|
relativeFilePath: "guidelines.md"
|
|
12061
12065
|
},
|
|
12062
12066
|
nonRoot: {
|
|
12063
|
-
relativeDirPath:
|
|
12067
|
+
relativeDirPath: buildToolPath(".junie", "memories", _options.excludeToolDir)
|
|
12064
12068
|
}
|
|
12065
12069
|
};
|
|
12066
12070
|
}
|
|
@@ -12131,7 +12135,7 @@ var KiloRule = class _KiloRule extends ToolRule {
|
|
|
12131
12135
|
static getSettablePaths(_options = {}) {
|
|
12132
12136
|
return {
|
|
12133
12137
|
nonRoot: {
|
|
12134
|
-
relativeDirPath:
|
|
12138
|
+
relativeDirPath: buildToolPath(".kilocode", "rules", _options.excludeToolDir)
|
|
12135
12139
|
}
|
|
12136
12140
|
};
|
|
12137
12141
|
}
|
|
@@ -12198,7 +12202,7 @@ var KiroRule = class _KiroRule extends ToolRule {
|
|
|
12198
12202
|
static getSettablePaths(_options = {}) {
|
|
12199
12203
|
return {
|
|
12200
12204
|
nonRoot: {
|
|
12201
|
-
relativeDirPath:
|
|
12205
|
+
relativeDirPath: buildToolPath(".kiro", "steering", _options.excludeToolDir)
|
|
12202
12206
|
}
|
|
12203
12207
|
};
|
|
12204
12208
|
}
|
|
@@ -12271,7 +12275,7 @@ var OpenCodeRule = class _OpenCodeRule extends ToolRule {
|
|
|
12271
12275
|
relativeFilePath: "AGENTS.md"
|
|
12272
12276
|
},
|
|
12273
12277
|
nonRoot: {
|
|
12274
|
-
relativeDirPath:
|
|
12278
|
+
relativeDirPath: buildToolPath(".opencode", "memories", _options.excludeToolDir)
|
|
12275
12279
|
}
|
|
12276
12280
|
};
|
|
12277
12281
|
}
|
|
@@ -12346,7 +12350,7 @@ var QwencodeRule = class _QwencodeRule extends ToolRule {
|
|
|
12346
12350
|
relativeFilePath: "QWEN.md"
|
|
12347
12351
|
},
|
|
12348
12352
|
nonRoot: {
|
|
12349
|
-
relativeDirPath:
|
|
12353
|
+
relativeDirPath: buildToolPath(".qwen", "memories", _options.excludeToolDir)
|
|
12350
12354
|
}
|
|
12351
12355
|
};
|
|
12352
12356
|
}
|
|
@@ -12502,7 +12506,7 @@ var RooRule = class _RooRule extends ToolRule {
|
|
|
12502
12506
|
static getSettablePaths(_options = {}) {
|
|
12503
12507
|
return {
|
|
12504
12508
|
nonRoot: {
|
|
12505
|
-
relativeDirPath:
|
|
12509
|
+
relativeDirPath: buildToolPath(".roo", "rules", _options.excludeToolDir)
|
|
12506
12510
|
}
|
|
12507
12511
|
};
|
|
12508
12512
|
}
|
|
@@ -12597,7 +12601,7 @@ var WarpRule = class _WarpRule extends ToolRule {
|
|
|
12597
12601
|
relativeFilePath: "WARP.md"
|
|
12598
12602
|
},
|
|
12599
12603
|
nonRoot: {
|
|
12600
|
-
relativeDirPath:
|
|
12604
|
+
relativeDirPath: buildToolPath(".warp", "memories", _options.excludeToolDir)
|
|
12601
12605
|
}
|
|
12602
12606
|
};
|
|
12603
12607
|
}
|
|
@@ -12668,7 +12672,7 @@ var WindsurfRule = class _WindsurfRule extends ToolRule {
|
|
|
12668
12672
|
static getSettablePaths(_options = {}) {
|
|
12669
12673
|
return {
|
|
12670
12674
|
nonRoot: {
|
|
12671
|
-
relativeDirPath:
|
|
12675
|
+
relativeDirPath: buildToolPath(".windsurf", "rules", _options.excludeToolDir)
|
|
12672
12676
|
}
|
|
12673
12677
|
};
|
|
12674
12678
|
}
|
|
@@ -13520,6 +13524,17 @@ var GitHubClientError = class extends Error {
|
|
|
13520
13524
|
this.name = "GitHubClientError";
|
|
13521
13525
|
}
|
|
13522
13526
|
};
|
|
13527
|
+
function logGitHubAuthHints(error) {
|
|
13528
|
+
logger.error(`GitHub API Error: ${error.message}`);
|
|
13529
|
+
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
13530
|
+
logger.info(
|
|
13531
|
+
"Tip: Set GITHUB_TOKEN or GH_TOKEN environment variable for private repositories or better rate limits."
|
|
13532
|
+
);
|
|
13533
|
+
logger.info(
|
|
13534
|
+
"Tip: If you use GitHub CLI, you can use `GITHUB_TOKEN=$(gh auth token) rulesync fetch ...`"
|
|
13535
|
+
);
|
|
13536
|
+
}
|
|
13537
|
+
}
|
|
13523
13538
|
var GitHubClient = class {
|
|
13524
13539
|
octokit;
|
|
13525
13540
|
hasToken;
|
|
@@ -13834,13 +13849,25 @@ function parseSource(source) {
|
|
|
13834
13849
|
}
|
|
13835
13850
|
return { provider: "github", ...parseShorthand(source) };
|
|
13836
13851
|
}
|
|
13852
|
+
var GITHUB_HOSTS = /* @__PURE__ */ new Set(["github.com", "www.github.com"]);
|
|
13853
|
+
var GITLAB_HOSTS = /* @__PURE__ */ new Set(["gitlab.com", "www.gitlab.com"]);
|
|
13854
|
+
var MAX_RECURSION_DEPTH = 15;
|
|
13855
|
+
var FETCH_CONCURRENCY_LIMIT = 10;
|
|
13856
|
+
async function withSemaphore(semaphore, fn) {
|
|
13857
|
+
await semaphore.acquire();
|
|
13858
|
+
try {
|
|
13859
|
+
return await fn();
|
|
13860
|
+
} finally {
|
|
13861
|
+
semaphore.release();
|
|
13862
|
+
}
|
|
13863
|
+
}
|
|
13837
13864
|
function parseUrl(url) {
|
|
13838
13865
|
const urlObj = new URL(url);
|
|
13839
13866
|
const host = urlObj.hostname.toLowerCase();
|
|
13840
13867
|
let provider;
|
|
13841
|
-
if (
|
|
13868
|
+
if (GITHUB_HOSTS.has(host)) {
|
|
13842
13869
|
provider = "github";
|
|
13843
|
-
} else if (
|
|
13870
|
+
} else if (GITLAB_HOSTS.has(host)) {
|
|
13844
13871
|
provider = "gitlab";
|
|
13845
13872
|
} else {
|
|
13846
13873
|
throw new Error(
|
|
@@ -13973,13 +14000,15 @@ async function fetchFiles(params) {
|
|
|
13973
14000
|
conflictStrategy
|
|
13974
14001
|
});
|
|
13975
14002
|
}
|
|
14003
|
+
const semaphore = new Semaphore(FETCH_CONCURRENCY_LIMIT);
|
|
13976
14004
|
const filesToFetch = await collectFeatureFiles({
|
|
13977
14005
|
client,
|
|
13978
14006
|
owner: parsed.owner,
|
|
13979
14007
|
repo: parsed.repo,
|
|
13980
14008
|
basePath: resolvedPath,
|
|
13981
14009
|
ref,
|
|
13982
|
-
enabledFeatures
|
|
14010
|
+
enabledFeatures,
|
|
14011
|
+
semaphore
|
|
13983
14012
|
});
|
|
13984
14013
|
if (filesToFetch.length === 0) {
|
|
13985
14014
|
logger.warn(`No files found matching enabled features: ${enabledFeatures.join(", ")}`);
|
|
@@ -13992,9 +14021,8 @@ async function fetchFiles(params) {
|
|
|
13992
14021
|
skipped: 0
|
|
13993
14022
|
};
|
|
13994
14023
|
}
|
|
13995
|
-
const results = [];
|
|
13996
14024
|
const outputBasePath = join106(baseDir, outputDir);
|
|
13997
|
-
for (const {
|
|
14025
|
+
for (const { relativePath, size } of filesToFetch) {
|
|
13998
14026
|
checkPathTraversal({
|
|
13999
14027
|
relativePath,
|
|
14000
14028
|
intendedRootDir: outputBasePath
|
|
@@ -14004,20 +14032,25 @@ async function fetchFiles(params) {
|
|
|
14004
14032
|
`File "${relativePath}" exceeds maximum size limit (${(size / 1024 / 1024).toFixed(2)}MB > ${MAX_FILE_SIZE / 1024 / 1024}MB)`
|
|
14005
14033
|
);
|
|
14006
14034
|
}
|
|
14007
|
-
|
|
14008
|
-
|
|
14009
|
-
|
|
14010
|
-
|
|
14011
|
-
|
|
14012
|
-
|
|
14013
|
-
|
|
14014
|
-
|
|
14035
|
+
}
|
|
14036
|
+
const results = await Promise.all(
|
|
14037
|
+
filesToFetch.map(async ({ remotePath, relativePath }) => {
|
|
14038
|
+
const localPath = join106(outputBasePath, relativePath);
|
|
14039
|
+
const exists = await fileExists(localPath);
|
|
14040
|
+
if (exists && conflictStrategy === "skip") {
|
|
14041
|
+
logger.debug(`Skipping existing file: ${relativePath}`);
|
|
14042
|
+
return { relativePath, status: "skipped" };
|
|
14043
|
+
}
|
|
14044
|
+
const content = await withSemaphore(
|
|
14045
|
+
semaphore,
|
|
14046
|
+
() => client.getFileContent(parsed.owner, parsed.repo, remotePath, ref)
|
|
14047
|
+
);
|
|
14015
14048
|
await writeFileContent(localPath, content);
|
|
14016
|
-
status = exists ? "overwritten" : "created";
|
|
14049
|
+
const status = exists ? "overwritten" : "created";
|
|
14017
14050
|
logger.debug(`Wrote: ${relativePath} (${status})`);
|
|
14018
|
-
|
|
14019
|
-
|
|
14020
|
-
|
|
14051
|
+
return { relativePath, status };
|
|
14052
|
+
})
|
|
14053
|
+
);
|
|
14021
14054
|
const summary = {
|
|
14022
14055
|
source: `${parsed.owner}/${parsed.repo}`,
|
|
14023
14056
|
ref,
|
|
@@ -14029,24 +14062,29 @@ async function fetchFiles(params) {
|
|
|
14029
14062
|
return summary;
|
|
14030
14063
|
}
|
|
14031
14064
|
async function collectFeatureFiles(params) {
|
|
14032
|
-
const { client, owner, repo, basePath, ref, enabledFeatures } = params;
|
|
14033
|
-
const
|
|
14034
|
-
|
|
14035
|
-
|
|
14036
|
-
|
|
14065
|
+
const { client, owner, repo, basePath, ref, enabledFeatures, semaphore } = params;
|
|
14066
|
+
const tasks = enabledFeatures.flatMap(
|
|
14067
|
+
(feature) => FEATURE_PATHS[feature].map((featurePath) => ({ feature, featurePath }))
|
|
14068
|
+
);
|
|
14069
|
+
const results = await Promise.all(
|
|
14070
|
+
tasks.map(async ({ featurePath }) => {
|
|
14037
14071
|
const fullPath = basePath === "." || basePath === "" ? featurePath : join106(basePath, featurePath);
|
|
14072
|
+
const collected = [];
|
|
14038
14073
|
try {
|
|
14039
14074
|
if (featurePath.includes(".")) {
|
|
14040
14075
|
try {
|
|
14041
|
-
const entries = await
|
|
14042
|
-
|
|
14043
|
-
|
|
14044
|
-
|
|
14045
|
-
|
|
14076
|
+
const entries = await withSemaphore(
|
|
14077
|
+
semaphore,
|
|
14078
|
+
() => client.listDirectory(
|
|
14079
|
+
owner,
|
|
14080
|
+
repo,
|
|
14081
|
+
basePath === "." || basePath === "" ? "." : basePath,
|
|
14082
|
+
ref
|
|
14083
|
+
)
|
|
14046
14084
|
);
|
|
14047
14085
|
const fileEntry = entries.find((e) => e.name === featurePath && e.type === "file");
|
|
14048
14086
|
if (fileEntry) {
|
|
14049
|
-
|
|
14087
|
+
collected.push({
|
|
14050
14088
|
remotePath: fileEntry.path,
|
|
14051
14089
|
relativePath: featurePath,
|
|
14052
14090
|
size: fileEntry.size
|
|
@@ -14060,10 +14098,17 @@ async function collectFeatureFiles(params) {
|
|
|
14060
14098
|
}
|
|
14061
14099
|
}
|
|
14062
14100
|
} else {
|
|
14063
|
-
const dirFiles = await listDirectoryRecursive(
|
|
14101
|
+
const dirFiles = await listDirectoryRecursive({
|
|
14102
|
+
client,
|
|
14103
|
+
owner,
|
|
14104
|
+
repo,
|
|
14105
|
+
path: fullPath,
|
|
14106
|
+
ref,
|
|
14107
|
+
semaphore
|
|
14108
|
+
});
|
|
14064
14109
|
for (const file of dirFiles) {
|
|
14065
14110
|
const relativePath = basePath === "." || basePath === "" ? file.path : file.path.substring(basePath.length + 1);
|
|
14066
|
-
|
|
14111
|
+
collected.push({
|
|
14067
14112
|
remotePath: file.path,
|
|
14068
14113
|
relativePath,
|
|
14069
14114
|
size: file.size
|
|
@@ -14073,26 +14118,49 @@ async function collectFeatureFiles(params) {
|
|
|
14073
14118
|
} catch (error) {
|
|
14074
14119
|
if (isNotFoundError(error)) {
|
|
14075
14120
|
logger.debug(`Feature not found: ${fullPath}`);
|
|
14076
|
-
|
|
14121
|
+
return collected;
|
|
14077
14122
|
}
|
|
14078
14123
|
throw error;
|
|
14079
14124
|
}
|
|
14080
|
-
|
|
14081
|
-
|
|
14082
|
-
|
|
14125
|
+
return collected;
|
|
14126
|
+
})
|
|
14127
|
+
);
|
|
14128
|
+
return results.flat();
|
|
14083
14129
|
}
|
|
14084
|
-
async function listDirectoryRecursive(
|
|
14085
|
-
const
|
|
14130
|
+
async function listDirectoryRecursive(params) {
|
|
14131
|
+
const { client, owner, repo, path: path4, ref, depth = 0, semaphore } = params;
|
|
14132
|
+
if (depth > MAX_RECURSION_DEPTH) {
|
|
14133
|
+
throw new Error(
|
|
14134
|
+
`Maximum recursion depth (${MAX_RECURSION_DEPTH}) exceeded while listing directory: ${path4}`
|
|
14135
|
+
);
|
|
14136
|
+
}
|
|
14137
|
+
const entries = await withSemaphore(
|
|
14138
|
+
semaphore,
|
|
14139
|
+
() => client.listDirectory(owner, repo, path4, ref)
|
|
14140
|
+
);
|
|
14086
14141
|
const files = [];
|
|
14142
|
+
const directories = [];
|
|
14087
14143
|
for (const entry of entries) {
|
|
14088
14144
|
if (entry.type === "file") {
|
|
14089
14145
|
files.push(entry);
|
|
14090
14146
|
} else if (entry.type === "dir") {
|
|
14091
|
-
|
|
14092
|
-
files.push(...subFiles);
|
|
14147
|
+
directories.push(entry);
|
|
14093
14148
|
}
|
|
14094
14149
|
}
|
|
14095
|
-
|
|
14150
|
+
const subResults = await Promise.all(
|
|
14151
|
+
directories.map(
|
|
14152
|
+
(dir) => listDirectoryRecursive({
|
|
14153
|
+
client,
|
|
14154
|
+
owner,
|
|
14155
|
+
repo,
|
|
14156
|
+
path: dir.path,
|
|
14157
|
+
ref,
|
|
14158
|
+
depth: depth + 1,
|
|
14159
|
+
semaphore
|
|
14160
|
+
})
|
|
14161
|
+
)
|
|
14162
|
+
);
|
|
14163
|
+
return [...files, ...subResults.flat()];
|
|
14096
14164
|
}
|
|
14097
14165
|
async function fetchAndConvertToolFiles(params) {
|
|
14098
14166
|
const {
|
|
@@ -14108,6 +14176,7 @@ async function fetchAndConvertToolFiles(params) {
|
|
|
14108
14176
|
} = params;
|
|
14109
14177
|
const tempDir = await createTempDirectory();
|
|
14110
14178
|
logger.debug(`Created temp directory: ${tempDir}`);
|
|
14179
|
+
const semaphore = new Semaphore(FETCH_CONCURRENCY_LIMIT);
|
|
14111
14180
|
try {
|
|
14112
14181
|
const filesToFetch = await collectFeatureFiles({
|
|
14113
14182
|
client,
|
|
@@ -14115,7 +14184,8 @@ async function fetchAndConvertToolFiles(params) {
|
|
|
14115
14184
|
repo: parsed.repo,
|
|
14116
14185
|
basePath: resolvedPath,
|
|
14117
14186
|
ref,
|
|
14118
|
-
enabledFeatures
|
|
14187
|
+
enabledFeatures,
|
|
14188
|
+
semaphore
|
|
14119
14189
|
});
|
|
14120
14190
|
if (filesToFetch.length === 0) {
|
|
14121
14191
|
logger.warn(`No files found matching enabled features: ${enabledFeatures.join(", ")}`);
|
|
@@ -14128,25 +14198,31 @@ async function fetchAndConvertToolFiles(params) {
|
|
|
14128
14198
|
skipped: 0
|
|
14129
14199
|
};
|
|
14130
14200
|
}
|
|
14131
|
-
const
|
|
14132
|
-
const fetchedFiles = [];
|
|
14133
|
-
for (const { remotePath, relativePath, size } of filesToFetch) {
|
|
14201
|
+
for (const { relativePath, size } of filesToFetch) {
|
|
14134
14202
|
if (size > MAX_FILE_SIZE) {
|
|
14135
14203
|
throw new GitHubClientError(
|
|
14136
14204
|
`File "${relativePath}" exceeds maximum size limit (${(size / 1024 / 1024).toFixed(2)}MB > ${MAX_FILE_SIZE / 1024 / 1024}MB)`
|
|
14137
14205
|
);
|
|
14138
14206
|
}
|
|
14139
|
-
const toolRelativePath = mapToToolPath(relativePath, toolPaths);
|
|
14140
|
-
checkPathTraversal({
|
|
14141
|
-
relativePath: toolRelativePath,
|
|
14142
|
-
intendedRootDir: tempDir
|
|
14143
|
-
});
|
|
14144
|
-
const localPath = join106(tempDir, toolRelativePath);
|
|
14145
|
-
const content = await client.getFileContent(parsed.owner, parsed.repo, remotePath, ref);
|
|
14146
|
-
await writeFileContent(localPath, content);
|
|
14147
|
-
fetchedFiles.push(toolRelativePath);
|
|
14148
|
-
logger.debug(`Fetched to temp: ${toolRelativePath}`);
|
|
14149
14207
|
}
|
|
14208
|
+
const toolPaths = getToolPathMapping(target);
|
|
14209
|
+
await Promise.all(
|
|
14210
|
+
filesToFetch.map(async ({ remotePath, relativePath }) => {
|
|
14211
|
+
const toolRelativePath = mapToToolPath(relativePath, toolPaths);
|
|
14212
|
+
checkPathTraversal({
|
|
14213
|
+
relativePath: toolRelativePath,
|
|
14214
|
+
intendedRootDir: tempDir
|
|
14215
|
+
});
|
|
14216
|
+
const localPath = join106(tempDir, toolRelativePath);
|
|
14217
|
+
const content = await withSemaphore(
|
|
14218
|
+
semaphore,
|
|
14219
|
+
() => client.getFileContent(parsed.owner, parsed.repo, remotePath, ref)
|
|
14220
|
+
);
|
|
14221
|
+
await writeFileContent(localPath, content);
|
|
14222
|
+
logger.debug(`Fetched to temp: ${toolRelativePath}`);
|
|
14223
|
+
return toolRelativePath;
|
|
14224
|
+
})
|
|
14225
|
+
);
|
|
14150
14226
|
const outputBasePath = join106(baseDir, outputDir);
|
|
14151
14227
|
const { converted, convertedPaths } = await convertFetchedFilesToRulesync({
|
|
14152
14228
|
tempDir,
|
|
@@ -14284,12 +14360,7 @@ async function fetchCommand(options) {
|
|
|
14284
14360
|
}
|
|
14285
14361
|
} catch (error) {
|
|
14286
14362
|
if (error instanceof GitHubClientError) {
|
|
14287
|
-
|
|
14288
|
-
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
14289
|
-
logger.info(
|
|
14290
|
-
"Tip: Set GITHUB_TOKEN or GH_TOKEN environment variable for private repositories."
|
|
14291
|
-
);
|
|
14292
|
-
}
|
|
14363
|
+
logGitHubAuthHints(error);
|
|
14293
14364
|
} else {
|
|
14294
14365
|
logger.error(formatError(error));
|
|
14295
14366
|
}
|
|
@@ -17160,7 +17231,8 @@ var MAX_DOWNLOAD_SIZE = 500 * 1024 * 1024;
|
|
|
17160
17231
|
var ALLOWED_DOWNLOAD_DOMAINS = [
|
|
17161
17232
|
"github.com",
|
|
17162
17233
|
"objects.githubusercontent.com",
|
|
17163
|
-
"github-releases.githubusercontent.com"
|
|
17234
|
+
"github-releases.githubusercontent.com",
|
|
17235
|
+
"release-assets.githubusercontent.com"
|
|
17164
17236
|
];
|
|
17165
17237
|
var UpdatePermissionError = class extends Error {
|
|
17166
17238
|
constructor(message) {
|
|
@@ -17486,12 +17558,7 @@ async function updateCommand(currentVersion, options) {
|
|
|
17486
17558
|
logger.success(message);
|
|
17487
17559
|
} catch (error) {
|
|
17488
17560
|
if (error instanceof GitHubClientError) {
|
|
17489
|
-
|
|
17490
|
-
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
17491
|
-
logger.info(
|
|
17492
|
-
"Tip: Set GITHUB_TOKEN or GH_TOKEN environment variable for better rate limits."
|
|
17493
|
-
);
|
|
17494
|
-
}
|
|
17561
|
+
logGitHubAuthHints(error);
|
|
17495
17562
|
} else if (error instanceof UpdatePermissionError) {
|
|
17496
17563
|
logger.error(error.message);
|
|
17497
17564
|
logger.info("Tip: Run with elevated privileges (e.g., sudo rulesync update)");
|
|
@@ -17503,7 +17570,7 @@ async function updateCommand(currentVersion, options) {
|
|
|
17503
17570
|
}
|
|
17504
17571
|
|
|
17505
17572
|
// src/cli/index.ts
|
|
17506
|
-
var getVersion = () => "6.6.
|
|
17573
|
+
var getVersion = () => "6.6.2";
|
|
17507
17574
|
var main = async () => {
|
|
17508
17575
|
const program = new Command();
|
|
17509
17576
|
const version = getVersion();
|
|
@@ -17603,7 +17670,7 @@ var main = async () => {
|
|
|
17603
17670
|
).option(
|
|
17604
17671
|
"--modular-mcp",
|
|
17605
17672
|
"Generate modular-mcp configuration for context compression (experimental)"
|
|
17606
|
-
).option("--dry-run", "
|
|
17673
|
+
).option("--dry-run", "Dry run: show changes without writing files").option("--check", "Check if files are up to date (exits with code 1 if changes needed)").action(async (options) => {
|
|
17607
17674
|
try {
|
|
17608
17675
|
await generateCommand({
|
|
17609
17676
|
targets: options.targets,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rulesync",
|
|
3
|
-
"version": "6.6.
|
|
3
|
+
"version": "6.6.2",
|
|
4
4
|
"description": "Unified AI rules management CLI tool that generates configuration files for various AI development tools",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
"eslint-plugin-zod-import": "0.3.0",
|
|
73
73
|
"knip": "5.82.1",
|
|
74
74
|
"lint-staged": "16.2.7",
|
|
75
|
+
"marked": "17.0.1",
|
|
75
76
|
"oxfmt": "0.27.0",
|
|
76
77
|
"oxlint": "1.42.0",
|
|
77
78
|
"repomix": "1.11.1",
|