rulesync 36.0.2 → 37.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/dist/cli/index.cjs +83 -26
- package/dist/cli/index.js +83 -26
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-Tya9qZRK.js → import-BPSwSahy.js} +329 -111
- package/dist/{import-Tya9qZRK.js.map → import-BPSwSahy.js.map} +1 -1
- package/dist/{import-DVcemXlR.cjs → import-S70XttcD.cjs} +345 -127
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +17 -20
|
@@ -4040,7 +4040,7 @@ function isPlainObject$1(value) {
|
|
|
4040
4040
|
/**
|
|
4041
4041
|
* Type guard to check if a value is an array of strings.
|
|
4042
4042
|
*/
|
|
4043
|
-
function isStringArray$
|
|
4043
|
+
function isStringArray$1(value) {
|
|
4044
4044
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
4045
4045
|
}
|
|
4046
4046
|
//#endregion
|
|
@@ -12835,6 +12835,10 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
12835
12835
|
kind: "replace-owned-keys",
|
|
12836
12836
|
ownedKeys: ["disableAllHooks"]
|
|
12837
12837
|
},
|
|
12838
|
+
mcp: {
|
|
12839
|
+
kind: "replace-owned-keys",
|
|
12840
|
+
ownedKeys: ["disabledMcpServers"]
|
|
12841
|
+
},
|
|
12838
12842
|
permissions: {
|
|
12839
12843
|
kind: "replace-owned-keys",
|
|
12840
12844
|
ownedKeys: ["deniedUrls"]
|
|
@@ -12849,6 +12853,10 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
12849
12853
|
kind: "replace-owned-keys",
|
|
12850
12854
|
ownedKeys: ["disableAllHooks"]
|
|
12851
12855
|
},
|
|
12856
|
+
mcp: {
|
|
12857
|
+
kind: "replace-owned-keys",
|
|
12858
|
+
ownedKeys: ["disabledMcpServers"]
|
|
12859
|
+
},
|
|
12852
12860
|
permissions: {
|
|
12853
12861
|
kind: "replace-owned-keys",
|
|
12854
12862
|
ownedKeys: ["allowedUrls", "deniedUrls"]
|
|
@@ -22113,7 +22121,7 @@ function getOpencodeCommandEntries({ config, layout = OPENCODE_LAYOUT }) {
|
|
|
22113
22121
|
*/
|
|
22114
22122
|
function getOpencodeSkillPaths({ config, layout = OPENCODE_LAYOUT }) {
|
|
22115
22123
|
if (Array.isArray(config.skills)) {
|
|
22116
|
-
if (!layout.readsV2ConfigSpellings || !isStringArray$
|
|
22124
|
+
if (!layout.readsV2ConfigSpellings || !isStringArray$1(config.skills)) return [];
|
|
22117
22125
|
return config.skills.filter((value) => !/^https?:\/\//i.test(value));
|
|
22118
22126
|
}
|
|
22119
22127
|
const skills = asOpencodeEntries(config.skills);
|
|
@@ -26359,7 +26367,7 @@ const isBooleanValue = ({ value }) => typeof value === "boolean";
|
|
|
26359
26367
|
const isFiniteNumber = ({ value }) => Number.isFinite(value);
|
|
26360
26368
|
const isNonEmptyString = (value) => typeof value === "string" && value !== "";
|
|
26361
26369
|
const isEmittableString = ({ value }) => isNonEmptyString(value);
|
|
26362
|
-
const isEmittableArray = ({ value }) => isStringArray
|
|
26370
|
+
const isEmittableArray = ({ value }) => isStringArray(value);
|
|
26363
26371
|
const isEmittableRecord = ({ value }) => isSafeStringRecord(value);
|
|
26364
26372
|
const isImportableArray = ({ value }) => isSafeStringArray(value);
|
|
26365
26373
|
const isEmittableObject = ({ value }) => isPlainObject$1(value);
|
|
@@ -26801,7 +26809,7 @@ function isStringRecord(value) {
|
|
|
26801
26809
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
26802
26810
|
return Object.values(value).every((v) => typeof v === "string");
|
|
26803
26811
|
}
|
|
26804
|
-
function isStringArray
|
|
26812
|
+
function isStringArray(value) {
|
|
26805
26813
|
return Array.isArray(value) && value.every((v) => typeof v === "string");
|
|
26806
26814
|
}
|
|
26807
26815
|
/** Compare object values without letting key order decide the answer. */
|
|
@@ -26846,7 +26854,7 @@ function isSafeEnvEntry(entry) {
|
|
|
26846
26854
|
* skipped wholesale when that read fails.
|
|
26847
26855
|
*/
|
|
26848
26856
|
function isSafeStringArray(value) {
|
|
26849
|
-
return isStringArray
|
|
26857
|
+
return isStringArray(value) && value.every((entry) => !CONTROL_CHARS.some((char) => entry.includes(char)));
|
|
26850
26858
|
}
|
|
26851
26859
|
/**
|
|
26852
26860
|
* A raw string kept only if the canonical field it would land in accepts it,
|
|
@@ -26888,7 +26896,7 @@ function importTypePayloadFields({ h, hookType, warn }) {
|
|
|
26888
26896
|
return {
|
|
26889
26897
|
...url !== void 0 && { url },
|
|
26890
26898
|
...isStringRecord(headers) && { headers },
|
|
26891
|
-
...isStringArray
|
|
26899
|
+
...isStringArray(h.allowedEnvVars) && { allowedEnvVars: h.allowedEnvVars }
|
|
26892
26900
|
};
|
|
26893
26901
|
}
|
|
26894
26902
|
if (hookType === "mcp_tool") {
|
|
@@ -28049,7 +28057,8 @@ function mergeMatcherGroups({ existing, generated, event, seen, previouslyOwned,
|
|
|
28049
28057
|
logger
|
|
28050
28058
|
})).map((handler) => cloneHandler(handler));
|
|
28051
28059
|
if (preserved.length === 0) continue;
|
|
28052
|
-
const
|
|
28060
|
+
const attributes = groupAttributesKey(group);
|
|
28061
|
+
const target = merged.find((candidate) => isMatcherGroup(candidate) && matcherKey(candidate.matcher) === matcher && groupAttributesKey(candidate) === attributes);
|
|
28053
28062
|
if (target !== void 0 && isMatcherGroup(target)) target.hooks.push(...preserved);
|
|
28054
28063
|
else merged.push({
|
|
28055
28064
|
...omitPrototypePollutionKeys(group),
|
|
@@ -28170,6 +28179,11 @@ function stableStringify$1(value) {
|
|
|
28170
28179
|
function matcherKey(matcher) {
|
|
28171
28180
|
return matcher === void 0 ? "" : stableStringify$1(matcher);
|
|
28172
28181
|
}
|
|
28182
|
+
/** The stable form of a matcher group's keys other than `matcher` and `hooks`. */
|
|
28183
|
+
function groupAttributesKey(group) {
|
|
28184
|
+
const { matcher: _matcher, hooks: _hooks, ...attributes } = group;
|
|
28185
|
+
return stableStringify$1(attributes);
|
|
28186
|
+
}
|
|
28173
28187
|
function warnSkip({ logger, event, expected }) {
|
|
28174
28188
|
logger?.warn(`Skipping existing hook entry on ${event}: expected ${expected}`);
|
|
28175
28189
|
}
|
|
@@ -28665,7 +28679,7 @@ const MANAGED_EVENT_NAMES = new Set(Object.values(CANONICAL_TO_CLINE_EVENT_NAMES
|
|
|
28665
28679
|
function parseManifest(fileContent) {
|
|
28666
28680
|
try {
|
|
28667
28681
|
const parsed = JSON.parse(fileContent);
|
|
28668
|
-
if (!isRecord$1(parsed) || !isStringArray$
|
|
28682
|
+
if (!isRecord$1(parsed) || !isStringArray$1(parsed.events)) return null;
|
|
28669
28683
|
return {
|
|
28670
28684
|
generatedBy: "rulesync",
|
|
28671
28685
|
events: parsed.events.filter((event) => MANAGED_EVENT_NAMES.has(event))
|
|
@@ -30203,7 +30217,7 @@ function copilotCliHooksToCanonical(rawHooks, logger) {
|
|
|
30203
30217
|
* see the CLI config dir reference:
|
|
30204
30218
|
* https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference
|
|
30205
30219
|
*/
|
|
30206
|
-
function copilotCliSettingsPaths({ global }) {
|
|
30220
|
+
function copilotCliSettingsPaths$1({ global }) {
|
|
30207
30221
|
return global ? {
|
|
30208
30222
|
relativeDirPath: COPILOT_DIR,
|
|
30209
30223
|
relativeFilePath: COPILOTCLI_SETTINGS_FILE_NAME
|
|
@@ -30218,7 +30232,7 @@ function copilotCliSettingsPaths({ global }) {
|
|
|
30218
30232
|
* subject, and the permissions feature reports a broken settings file itself.
|
|
30219
30233
|
*/
|
|
30220
30234
|
async function readDisableAllHooks({ outputRoot, global }) {
|
|
30221
|
-
const paths = copilotCliSettingsPaths({ global });
|
|
30235
|
+
const paths = copilotCliSettingsPaths$1({ global });
|
|
30222
30236
|
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath));
|
|
30223
30237
|
if (fileContent === null) return;
|
|
30224
30238
|
try {
|
|
@@ -30248,7 +30262,7 @@ var CopilotcliHooksSettings = class CopilotcliHooksSettings extends ToolFile {
|
|
|
30248
30262
|
return false;
|
|
30249
30263
|
}
|
|
30250
30264
|
static async fromDisableAllHooks({ outputRoot = process.cwd(), global = false, disableAllHooks }) {
|
|
30251
|
-
const paths = copilotCliSettingsPaths({ global });
|
|
30265
|
+
const paths = copilotCliSettingsPaths$1({ global });
|
|
30252
30266
|
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
30253
30267
|
const existingContent = await readFileContentOrNull(filePath) ?? "{}";
|
|
30254
30268
|
return new CopilotcliHooksSettings({
|
|
@@ -30278,7 +30292,7 @@ var CopilotcliHooks = class CopilotcliHooks extends ToolHooks {
|
|
|
30278
30292
|
this.disableAllHooks = disableAllHooks;
|
|
30279
30293
|
}
|
|
30280
30294
|
static getExtraSharedWritePaths({ global = false } = {}) {
|
|
30281
|
-
return [copilotCliSettingsPaths({ global })];
|
|
30295
|
+
return [copilotCliSettingsPaths$1({ global })];
|
|
30282
30296
|
}
|
|
30283
30297
|
static async getAuxiliaryFiles({ toolHooks } = {}) {
|
|
30284
30298
|
return toolHooks instanceof CopilotcliHooks && toolHooks.settingsFile ? [toolHooks.settingsFile] : [];
|
|
@@ -34722,12 +34736,21 @@ var QwencodeHooks = class QwencodeHooks extends ToolHooks {
|
|
|
34722
34736
|
validate
|
|
34723
34737
|
});
|
|
34724
34738
|
}
|
|
34725
|
-
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, logger }) {
|
|
34739
|
+
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, preserveUnowned = false, logger }) {
|
|
34726
34740
|
const paths = QwencodeHooks.getSettablePaths({ global });
|
|
34727
34741
|
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
34728
34742
|
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
|
|
34729
34743
|
const config = rulesyncHooks.getJson();
|
|
34730
|
-
const
|
|
34744
|
+
const previouslyOwned = preserveUnowned ? parseHooksOwnershipLock(await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, HOOKS_OWNERSHIP_LOCK_FILE_NAME))) : void 0;
|
|
34745
|
+
const merged = mergeGeneratedHookLists({
|
|
34746
|
+
existingContent,
|
|
34747
|
+
generatedHooks: canonicalToQwencodeHooks(config, logger),
|
|
34748
|
+
shape: "matcher-groups",
|
|
34749
|
+
preserveUnowned,
|
|
34750
|
+
previouslyOwned,
|
|
34751
|
+
logger
|
|
34752
|
+
});
|
|
34753
|
+
const patch = { hooks: merged.hooks };
|
|
34731
34754
|
const disableAllHooks = config.qwencode?.disableAllHooks;
|
|
34732
34755
|
if (typeof disableAllHooks === "boolean") patch.disableAllHooks = disableAllHooks;
|
|
34733
34756
|
const fileContent = applySharedConfigPatch({
|
|
@@ -34742,9 +34765,16 @@ var QwencodeHooks = class QwencodeHooks extends ToolHooks {
|
|
|
34742
34765
|
relativeDirPath: paths.relativeDirPath,
|
|
34743
34766
|
relativeFilePath: paths.relativeFilePath,
|
|
34744
34767
|
fileContent,
|
|
34768
|
+
ownedHookRefs: preserveUnowned ? merged.owned : void 0,
|
|
34745
34769
|
validate
|
|
34746
34770
|
});
|
|
34747
34771
|
}
|
|
34772
|
+
static supportsPreserveUnowned() {
|
|
34773
|
+
return true;
|
|
34774
|
+
}
|
|
34775
|
+
static async getAuxiliaryFiles({ toolHooks } = {}) {
|
|
34776
|
+
return toolHooks instanceof QwencodeHooks ? toolHooks.getOwnershipLockFiles() : [];
|
|
34777
|
+
}
|
|
34748
34778
|
toRulesyncHooks() {
|
|
34749
34779
|
let settings;
|
|
34750
34780
|
try {
|
|
@@ -41144,7 +41174,7 @@ function isCodexStdioServer(config) {
|
|
|
41144
41174
|
* @see https://learn.chatgpt.com/docs/extend/mcp
|
|
41145
41175
|
*/
|
|
41146
41176
|
function isValidRenamedArray(key, value) {
|
|
41147
|
-
return key === "env_vars" || key === "envVars" ? isEnvVarEntryArray(value) : isStringArray$
|
|
41177
|
+
return key === "env_vars" || key === "envVars" ? isEnvVarEntryArray(value) : isStringArray$1(value);
|
|
41148
41178
|
}
|
|
41149
41179
|
/**
|
|
41150
41180
|
* The keys of Codex's `McpServerOAuthConfig`. Anything else in a server's
|
|
@@ -42363,18 +42393,6 @@ function renameEnabledToolsToTools({ server, serverName, logger }) {
|
|
|
42363
42393
|
};
|
|
42364
42394
|
}
|
|
42365
42395
|
/**
|
|
42366
|
-
* Copilot CLI's `tools` accepts more than the canonical `enabledTools` shape:
|
|
42367
|
-
* `--tools` documents `*`, a comma-separated list, or `""`, so a hand-written or
|
|
42368
|
-
* CLI-written entry can be a bare string. Canonical `enabledTools` is
|
|
42369
|
-
* `string[]`, and `RulesyncMcp` throws on a schema failure — carrying a string
|
|
42370
|
-
* through would reject the WHOLE imported MCP file rather than one key. Anything
|
|
42371
|
-
* that is not a string array is therefore left under `tools` untouched, matching
|
|
42372
|
-
* `codexcli-mcp.ts`'s `isValidRenamedArray` guard on the same class of rename.
|
|
42373
|
-
*/
|
|
42374
|
-
function isStringArray(value) {
|
|
42375
|
-
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
42376
|
-
}
|
|
42377
|
-
/**
|
|
42378
42396
|
* Resolves and sets the transport type for each MCP server config.
|
|
42379
42397
|
* GitHub Copilot CLI requires the "type" field for each server. A server it
|
|
42380
42398
|
* cannot express — one that names no transport, or names one it carries no way
|
|
@@ -42416,7 +42434,7 @@ function addTypeField(mcpServers, logger) {
|
|
|
42416
42434
|
});
|
|
42417
42435
|
continue;
|
|
42418
42436
|
}
|
|
42419
|
-
const { httpUrl: _httpUrl, ...rest } = parsed;
|
|
42437
|
+
const { httpUrl: _httpUrl, disabled: _disabled, ...rest } = parsed;
|
|
42420
42438
|
result[name] = {
|
|
42421
42439
|
...renameEnabledToolsToTools({
|
|
42422
42440
|
server: rest,
|
|
@@ -42428,7 +42446,7 @@ function addTypeField(mcpServers, logger) {
|
|
|
42428
42446
|
};
|
|
42429
42447
|
continue;
|
|
42430
42448
|
}
|
|
42431
|
-
const { httpUrl: _localHttpUrl, ...local } = parsed;
|
|
42449
|
+
const { httpUrl: _localHttpUrl, disabled: _localDisabled, ...local } = parsed;
|
|
42432
42450
|
const [head, ...tail] = resolveLocalMcpCommand(parsed);
|
|
42433
42451
|
if (head === void 0) {
|
|
42434
42452
|
warnAndSkipMcpServer({
|
|
@@ -42459,7 +42477,7 @@ function removeTypeField(config) {
|
|
|
42459
42477
|
const result = {};
|
|
42460
42478
|
for (const [name, server] of Object.entries(config.mcpServers ?? {})) {
|
|
42461
42479
|
const { tools, ...withoutTools } = server;
|
|
42462
|
-
const restored = isStringArray(tools) ? {
|
|
42480
|
+
const restored = isStringArray$1(tools) ? {
|
|
42463
42481
|
...withoutTools,
|
|
42464
42482
|
enabledTools: tools
|
|
42465
42483
|
} : {
|
|
@@ -42475,6 +42493,99 @@ function removeTypeField(config) {
|
|
|
42475
42493
|
}
|
|
42476
42494
|
return result;
|
|
42477
42495
|
}
|
|
42496
|
+
/**
|
|
42497
|
+
* The Copilot CLI settings file carrying `disabledMcpServers`:
|
|
42498
|
+
* `.github/copilot/settings.json` (project) / `~/.copilot/settings.json`
|
|
42499
|
+
* (global). Both scopes document the key ("MCP server names to disable. Listed
|
|
42500
|
+
* servers are configured but not started"); at repository scope it is
|
|
42501
|
+
* union-merged with the user value, so a repository can add names but never
|
|
42502
|
+
* remove them.
|
|
42503
|
+
*
|
|
42504
|
+
* @see https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference
|
|
42505
|
+
*/
|
|
42506
|
+
function copilotCliSettingsPaths({ global }) {
|
|
42507
|
+
return global ? {
|
|
42508
|
+
relativeDirPath: COPILOT_DIR,
|
|
42509
|
+
relativeFilePath: COPILOTCLI_SETTINGS_FILE_NAME
|
|
42510
|
+
} : {
|
|
42511
|
+
relativeDirPath: COPILOTCLI_PROJECT_SETTINGS_DIR_PATH,
|
|
42512
|
+
relativeFilePath: COPILOTCLI_SETTINGS_FILE_NAME
|
|
42513
|
+
};
|
|
42514
|
+
}
|
|
42515
|
+
async function readCopilotCliSettings({ outputRoot, global }) {
|
|
42516
|
+
const paths = copilotCliSettingsPaths({ global });
|
|
42517
|
+
const existingContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "";
|
|
42518
|
+
try {
|
|
42519
|
+
return {
|
|
42520
|
+
paths,
|
|
42521
|
+
existingContent,
|
|
42522
|
+
document: parseSharedConfig({
|
|
42523
|
+
format: "json",
|
|
42524
|
+
fileContent: existingContent,
|
|
42525
|
+
filePath: join(paths.relativeDirPath, paths.relativeFilePath),
|
|
42526
|
+
invalidRootPolicy: "error"
|
|
42527
|
+
})
|
|
42528
|
+
};
|
|
42529
|
+
} catch (error) {
|
|
42530
|
+
return {
|
|
42531
|
+
paths,
|
|
42532
|
+
existingContent,
|
|
42533
|
+
document: null,
|
|
42534
|
+
error
|
|
42535
|
+
};
|
|
42536
|
+
}
|
|
42537
|
+
}
|
|
42538
|
+
/**
|
|
42539
|
+
* The names of the servers rulesync actually writes for this target: one
|
|
42540
|
+
* `addTypeField` skips (no transport, WebSocket, missing url or command) is
|
|
42541
|
+
* not managed, so its name neither gains a toggle nor strips a same-named
|
|
42542
|
+
* user-authored entry from the settings file.
|
|
42543
|
+
*/
|
|
42544
|
+
function managedServerNames(mcpServers) {
|
|
42545
|
+
return Object.keys(addTypeField(mcpServers) ?? {});
|
|
42546
|
+
}
|
|
42547
|
+
/**
|
|
42548
|
+
* Overlays `disabled: true` on the servers the scope's settings file lists in
|
|
42549
|
+
* `disabledMcpServers`, so import round-trips the toggle into the canonical
|
|
42550
|
+
* config. A settings file that exists but cannot be parsed throws
|
|
42551
|
+
* (fail-closed): importing past it would re-enable every disabled server.
|
|
42552
|
+
*/
|
|
42553
|
+
async function overlayDisabledServers({ mcpServers, outputRoot, global }) {
|
|
42554
|
+
const settings = await readCopilotCliSettings({
|
|
42555
|
+
outputRoot,
|
|
42556
|
+
global
|
|
42557
|
+
});
|
|
42558
|
+
if (settings.document === null) throw new Error(`Cannot import Copilot CLI MCP servers: ${formatError(settings.error)}. Fix the file so disabledMcpServers can be read.`, { cause: settings.error });
|
|
42559
|
+
const disabled = settings.document.disabledMcpServers;
|
|
42560
|
+
if (!isStringArray$1(disabled) || disabled.length === 0) return mcpServers;
|
|
42561
|
+
const servers = { ...mcpServers };
|
|
42562
|
+
for (const name of disabled) {
|
|
42563
|
+
if (!Object.hasOwn(servers, name)) continue;
|
|
42564
|
+
const server = servers[name];
|
|
42565
|
+
if (isPlainObject$1(server)) servers[name] = {
|
|
42566
|
+
...server,
|
|
42567
|
+
disabled: true
|
|
42568
|
+
};
|
|
42569
|
+
}
|
|
42570
|
+
return servers;
|
|
42571
|
+
}
|
|
42572
|
+
/**
|
|
42573
|
+
* Auxiliary writer for `disabledMcpServers` in the Copilot CLI settings file.
|
|
42574
|
+
* Only that key is owned (through the shared-config gateway), so `deniedUrls`,
|
|
42575
|
+
* `disableAllHooks`, `model` and every other setting are preserved. It is a
|
|
42576
|
+
* shared, user-owned file and is never deleted.
|
|
42577
|
+
*/
|
|
42578
|
+
var CopilotcliMcpSettings = class extends ToolFile {
|
|
42579
|
+
validate() {
|
|
42580
|
+
return {
|
|
42581
|
+
success: true,
|
|
42582
|
+
error: null
|
|
42583
|
+
};
|
|
42584
|
+
}
|
|
42585
|
+
isDeletable() {
|
|
42586
|
+
return false;
|
|
42587
|
+
}
|
|
42588
|
+
};
|
|
42478
42589
|
var CopilotcliMcp = class CopilotcliMcp extends ToolMcp {
|
|
42479
42590
|
json;
|
|
42480
42591
|
constructor(params) {
|
|
@@ -42517,7 +42628,11 @@ var CopilotcliMcp = class CopilotcliMcp extends ToolMcp {
|
|
|
42517
42628
|
const json = JSON.parse(fileContent);
|
|
42518
42629
|
const newJson = {
|
|
42519
42630
|
...json,
|
|
42520
|
-
mcpServers:
|
|
42631
|
+
mcpServers: await overlayDisabledServers({
|
|
42632
|
+
mcpServers: json.mcpServers ?? {},
|
|
42633
|
+
outputRoot,
|
|
42634
|
+
global
|
|
42635
|
+
})
|
|
42521
42636
|
};
|
|
42522
42637
|
return new CopilotcliMcp({
|
|
42523
42638
|
outputRoot,
|
|
@@ -42532,7 +42647,15 @@ var CopilotcliMcp = class CopilotcliMcp extends ToolMcp {
|
|
|
42532
42647
|
const paths = this.getSettablePaths({ global });
|
|
42533
42648
|
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? JSON.stringify({ mcpServers: {} }, null, 2);
|
|
42534
42649
|
const json = JSON.parse(fileContent);
|
|
42535
|
-
const
|
|
42650
|
+
const settings = await readCopilotCliSettings({
|
|
42651
|
+
outputRoot,
|
|
42652
|
+
global
|
|
42653
|
+
});
|
|
42654
|
+
const copilotCliMcpServers = addTypeField(Object.fromEntries(Object.entries(rulesyncMcp.getMcpServers()).filter(([name, server]) => {
|
|
42655
|
+
if (server.disabled !== true || settings.document !== null) return true;
|
|
42656
|
+
logger?.warn(`[CopilotcliMcp] Skipping disabled MCP server "${name}": ${join(settings.paths.relativeDirPath, settings.paths.relativeFilePath)} cannot be parsed, so disabledMcpServers cannot be written to switch it off.`);
|
|
42657
|
+
return false;
|
|
42658
|
+
})), logger);
|
|
42536
42659
|
const mcpJson = {
|
|
42537
42660
|
...json,
|
|
42538
42661
|
mcpServers: copilotCliMcpServers
|
|
@@ -42546,6 +42669,61 @@ var CopilotcliMcp = class CopilotcliMcp extends ToolMcp {
|
|
|
42546
42669
|
global
|
|
42547
42670
|
});
|
|
42548
42671
|
}
|
|
42672
|
+
/**
|
|
42673
|
+
* `disabledMcpServers` lives in the Copilot CLI settings file the hooks and
|
|
42674
|
+
* permissions features also write. Declared here so the write-order
|
|
42675
|
+
* derivation sees this feature as one of that file's writers — it is not a
|
|
42676
|
+
* settable path, since the servers themselves live in the MCP file.
|
|
42677
|
+
*/
|
|
42678
|
+
static getExtraSharedWritePaths({ global = false } = {}) {
|
|
42679
|
+
return [copilotCliSettingsPaths({ global })];
|
|
42680
|
+
}
|
|
42681
|
+
/**
|
|
42682
|
+
* Routes the canonical `disabled: true` to `disabledMcpServers`, the key
|
|
42683
|
+
* Copilot CLI actually consults: an MCP file entry has no `disabled` field.
|
|
42684
|
+
* rulesync owns the toggle for the servers it writes; names it does not
|
|
42685
|
+
* manage keep their existing state. The file is not created or touched when
|
|
42686
|
+
* there is nothing to disable and nothing to retract.
|
|
42687
|
+
*/
|
|
42688
|
+
static async getAuxiliaryFiles({ outputRoot = process.cwd(), global = false, rulesyncMcp, logger }) {
|
|
42689
|
+
const servers = rulesyncMcp.forTarget({
|
|
42690
|
+
toolTarget: "copilotcli",
|
|
42691
|
+
global
|
|
42692
|
+
}).getMcpServers();
|
|
42693
|
+
const managedNames = managedServerNames(servers);
|
|
42694
|
+
const disabledNames = managedNames.filter((name) => servers[name]?.disabled === true);
|
|
42695
|
+
const settings = await readCopilotCliSettings({
|
|
42696
|
+
outputRoot,
|
|
42697
|
+
global
|
|
42698
|
+
});
|
|
42699
|
+
if (settings.document === null) {
|
|
42700
|
+
logger?.warn(`Skipping the Copilot CLI disabledMcpServers update: ${formatError(settings.error)}`);
|
|
42701
|
+
return [];
|
|
42702
|
+
}
|
|
42703
|
+
const existing = settings.document.disabledMcpServers;
|
|
42704
|
+
const existingDisabled = isStringArray$1(existing) ? existing : [];
|
|
42705
|
+
const managedNameSet = new Set(managedNames);
|
|
42706
|
+
const reEnabled = existingDisabled.filter((name) => managedNameSet.has(name) && !disabledNames.includes(name));
|
|
42707
|
+
if (reEnabled.length > 0) logger?.warn(`Copilot CLI MCP: re-enabling ${reEnabled.join(", ")} — the canonical config does not mark ${reEnabled.length === 1 ? "it" : "them"} disabled. Set "disabled": true in .rulesync/mcp.jsonc to keep a managed server off.`);
|
|
42708
|
+
const mergedDisabled = [...existingDisabled.filter((name) => !managedNameSet.has(name)), ...disabledNames].toSorted();
|
|
42709
|
+
if (mergedDisabled.length === 0 && existing === void 0) return [];
|
|
42710
|
+
if (isStringArray$1(existing) && existing.length === mergedDisabled.length && existing.toSorted().every((name, index) => name === mergedDisabled[index])) return [];
|
|
42711
|
+
const { paths } = settings;
|
|
42712
|
+
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
42713
|
+
return [new CopilotcliMcpSettings({
|
|
42714
|
+
outputRoot,
|
|
42715
|
+
relativeDirPath: paths.relativeDirPath,
|
|
42716
|
+
relativeFilePath: paths.relativeFilePath,
|
|
42717
|
+
fileContent: applySharedConfigPatch({
|
|
42718
|
+
fileKey: sharedConfigFileKey(paths),
|
|
42719
|
+
feature: "mcp",
|
|
42720
|
+
existingContent: settings.existingContent,
|
|
42721
|
+
patch: { disabledMcpServers: mergedDisabled.length > 0 ? mergedDisabled : void 0 },
|
|
42722
|
+
filePath
|
|
42723
|
+
}),
|
|
42724
|
+
global
|
|
42725
|
+
})];
|
|
42726
|
+
}
|
|
42549
42727
|
toRulesyncMcp() {
|
|
42550
42728
|
const mcpServers = removeTypeField(this.json);
|
|
42551
42729
|
return this.toRulesyncMcpDefault({ fileContent: JSON.stringify({ mcpServers }, null, 2) });
|
|
@@ -42977,10 +43155,10 @@ function convertFromCrushFormat(crushServers) {
|
|
|
42977
43155
|
if (value === "http" || value === "sse") converted.type = value;
|
|
42978
43156
|
break;
|
|
42979
43157
|
case "disabled_tools":
|
|
42980
|
-
if (isStringArray$
|
|
43158
|
+
if (isStringArray$1(value)) converted.disabledTools = value;
|
|
42981
43159
|
break;
|
|
42982
43160
|
case "enabled_tools":
|
|
42983
|
-
if (isStringArray$
|
|
43161
|
+
if (isStringArray$1(value)) converted.enabledTools = value;
|
|
42984
43162
|
break;
|
|
42985
43163
|
case "oauth_token": break;
|
|
42986
43164
|
default: {
|
|
@@ -43967,7 +44145,7 @@ function convertRowsFromDsh(rows, logger) {
|
|
|
43967
44145
|
} else {
|
|
43968
44146
|
server.type = "stdio";
|
|
43969
44147
|
if (typeof config.command === "string") server.command = config.command;
|
|
43970
|
-
if (isStringArray$
|
|
44148
|
+
if (isStringArray$1(config.args)) server.args = config.args;
|
|
43971
44149
|
const env = copyStringRecord(config.env);
|
|
43972
44150
|
if (env !== void 0) server.env = env;
|
|
43973
44151
|
if (typeof config.cwd === "string") server.cwd = config.cwd;
|
|
@@ -44398,11 +44576,11 @@ function applyGooseStdioFields(ext, config) {
|
|
|
44398
44576
|
if (Array.isArray(command)) {
|
|
44399
44577
|
if (typeof command[0] === "string") ext.cmd = command[0];
|
|
44400
44578
|
const rest = command.slice(1).filter((c) => typeof c === "string");
|
|
44401
|
-
const args = isStringArray$
|
|
44579
|
+
const args = isStringArray$1(config.args) ? config.args : [];
|
|
44402
44580
|
if (rest.length > 0 || args.length > 0) ext.args = [...rest, ...args];
|
|
44403
44581
|
} else if (typeof command === "string") {
|
|
44404
44582
|
ext.cmd = command;
|
|
44405
|
-
if (isStringArray$
|
|
44583
|
+
if (isStringArray$1(config.args)) ext.args = config.args;
|
|
44406
44584
|
}
|
|
44407
44585
|
if (isPlainObject$1(config.env)) ext.envs = omitPrototypePollutionKeys(config.env);
|
|
44408
44586
|
if (typeof config.cwd === "string") ext.cwd = config.cwd;
|
|
@@ -44505,7 +44683,7 @@ function convertFromGooseFormat(extensions) {
|
|
|
44505
44683
|
else if (type === "streamable_http") server.type = "http";
|
|
44506
44684
|
else server.type = "stdio";
|
|
44507
44685
|
if (typeof ext.cmd === "string") server.command = ext.cmd;
|
|
44508
|
-
if (isStringArray$
|
|
44686
|
+
if (isStringArray$1(ext.args)) server.args = ext.args;
|
|
44509
44687
|
if (isPlainObject$1(ext.envs)) server.env = omitPrototypePollutionKeys(ext.envs);
|
|
44510
44688
|
if (typeof ext.cwd === "string") server.cwd = ext.cwd;
|
|
44511
44689
|
if (typeof ext.uri === "string") server.url = ext.uri;
|
|
@@ -44527,11 +44705,11 @@ function buildGoosePluginStdioServer(config) {
|
|
|
44527
44705
|
if (Array.isArray(command)) {
|
|
44528
44706
|
if (typeof command[0] === "string") server.command = command[0];
|
|
44529
44707
|
const rest = command.slice(1).filter((c) => typeof c === "string");
|
|
44530
|
-
const args = isStringArray$
|
|
44708
|
+
const args = isStringArray$1(config.args) ? config.args : [];
|
|
44531
44709
|
if (rest.length > 0 || args.length > 0) server.args = [...rest, ...args];
|
|
44532
44710
|
} else if (typeof command === "string") {
|
|
44533
44711
|
server.command = command;
|
|
44534
|
-
if (isStringArray$
|
|
44712
|
+
if (isStringArray$1(config.args)) server.args = config.args;
|
|
44535
44713
|
}
|
|
44536
44714
|
if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
44537
44715
|
if (typeof config.cwd === "string") server.cwd = config.cwd;
|
|
@@ -44939,7 +45117,7 @@ function copyHermesOauth(source) {
|
|
|
44939
45117
|
if (!isRecord$1(source)) return;
|
|
44940
45118
|
const oauth = {};
|
|
44941
45119
|
for (const key of HERMES_OAUTH_STRING_KEYS) if (typeof source[key] === "string") oauth[key] = source[key];
|
|
44942
|
-
if (typeof oauth.scope !== "string" && isStringArray$
|
|
45120
|
+
if (typeof oauth.scope !== "string" && isStringArray$1(source.scopes)) oauth.scope = source.scopes.join(" ");
|
|
44943
45121
|
for (const key of ["redirect_port", "timeout"]) if (typeof source[key] === "number") oauth[key] = source[key];
|
|
44944
45122
|
if (typeof source.cimd === "boolean") oauth.cimd = source.cimd;
|
|
44945
45123
|
return Object.keys(oauth).length > 0 ? oauth : void 0;
|
|
@@ -44966,7 +45144,7 @@ function copyHermesAdvancedFields(source, target) {
|
|
|
44966
45144
|
target.auth = source.auth;
|
|
44967
45145
|
copied = true;
|
|
44968
45146
|
}
|
|
44969
|
-
if (typeof source.client_cert === "string" || isStringArray$
|
|
45147
|
+
if (typeof source.client_cert === "string" || isStringArray$1(source.client_cert)) {
|
|
44970
45148
|
target.client_cert = source.client_cert;
|
|
44971
45149
|
copied = true;
|
|
44972
45150
|
}
|
|
@@ -45038,8 +45216,8 @@ function copyHermesAdvancedFields(source, target) {
|
|
|
45038
45216
|
*/
|
|
45039
45217
|
function buildHermesToolsBlock(config) {
|
|
45040
45218
|
const tools = {};
|
|
45041
|
-
if (isStringArray$
|
|
45042
|
-
if (isStringArray$
|
|
45219
|
+
if (isStringArray$1(config.enabledTools)) tools.include = config.enabledTools;
|
|
45220
|
+
if (isStringArray$1(config.disabledTools)) tools.exclude = config.disabledTools;
|
|
45043
45221
|
if (typeof config.promptsEnabled === "boolean") tools.prompts = config.promptsEnabled;
|
|
45044
45222
|
if (typeof config.resourcesEnabled === "boolean") tools.resources = config.resourcesEnabled;
|
|
45045
45223
|
return tools;
|
|
@@ -45051,8 +45229,8 @@ function buildHermesToolsBlock(config) {
|
|
|
45051
45229
|
* `promptsEnabled`/`resourcesEnabled` top-level toggles.
|
|
45052
45230
|
*/
|
|
45053
45231
|
function applyHermesToolsBlock(hermesTools, server) {
|
|
45054
|
-
if (isStringArray$
|
|
45055
|
-
if (isStringArray$
|
|
45232
|
+
if (isStringArray$1(hermesTools.include)) server.enabledTools = hermesTools.include;
|
|
45233
|
+
if (isStringArray$1(hermesTools.exclude)) server.disabledTools = hermesTools.exclude;
|
|
45056
45234
|
if (typeof hermesTools.prompts === "boolean") server.promptsEnabled = hermesTools.prompts;
|
|
45057
45235
|
if (typeof hermesTools.resources === "boolean") server.resourcesEnabled = hermesTools.resources;
|
|
45058
45236
|
}
|
|
@@ -45078,11 +45256,11 @@ function convertServerToHermes(config) {
|
|
|
45078
45256
|
if (Array.isArray(command)) {
|
|
45079
45257
|
if (typeof command[0] === "string") out.command = command[0];
|
|
45080
45258
|
const rest = command.slice(1).filter((c) => typeof c === "string");
|
|
45081
|
-
const args = isStringArray$
|
|
45259
|
+
const args = isStringArray$1(config.args) ? config.args : [];
|
|
45082
45260
|
if (rest.length > 0 || args.length > 0) out.args = [...rest, ...args];
|
|
45083
45261
|
} else if (typeof command === "string") {
|
|
45084
45262
|
out.command = command;
|
|
45085
|
-
if (isStringArray$
|
|
45263
|
+
if (isStringArray$1(config.args)) out.args = config.args;
|
|
45086
45264
|
}
|
|
45087
45265
|
if (isPlainObject$1(config.env)) out.env = omitPrototypePollutionKeys(config.env);
|
|
45088
45266
|
} else if (url !== void 0) {
|
|
@@ -45132,7 +45310,7 @@ function convertFromHermesFormat(mcpServers) {
|
|
|
45132
45310
|
if (PROTOTYPE_POLLUTION_KEYS.has(name) || !isRecord$1(config)) continue;
|
|
45133
45311
|
const server = {};
|
|
45134
45312
|
if (typeof config.command === "string") server.command = config.command;
|
|
45135
|
-
if (isStringArray$
|
|
45313
|
+
if (isStringArray$1(config.args)) server.args = config.args;
|
|
45136
45314
|
if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
45137
45315
|
if (typeof config.url === "string") server.url = config.url;
|
|
45138
45316
|
if (isPlainObject$1(config.headers)) server.headers = omitPrototypePollutionKeys(config.headers);
|
|
@@ -46324,7 +46502,7 @@ function mergeToolLists(...lists) {
|
|
|
46324
46502
|
function toKiroMcpServers(servers) {
|
|
46325
46503
|
return Object.fromEntries(Object.entries(servers).map(([name, server]) => {
|
|
46326
46504
|
const { kiroAutoApprove, kiroAutoBlock, disabledTools, ...rest } = server;
|
|
46327
|
-
const autoApprove = mergeToolLists(isStringArray$
|
|
46505
|
+
const autoApprove = mergeToolLists(isStringArray$1(rest.autoApprove) ? rest.autoApprove : void 0, kiroAutoApprove);
|
|
46328
46506
|
const disabled = mergeToolLists(disabledTools, kiroAutoBlock);
|
|
46329
46507
|
return [name, {
|
|
46330
46508
|
...rest,
|
|
@@ -46348,7 +46526,7 @@ function fromKiroMcpServers(servers) {
|
|
|
46348
46526
|
return Object.fromEntries(Object.entries(servers).map(([name, server]) => {
|
|
46349
46527
|
if (server === null || typeof server !== "object" || Array.isArray(server)) return [name, server];
|
|
46350
46528
|
const { autoApprove, ...rest } = server;
|
|
46351
|
-
if (!isStringArray$
|
|
46529
|
+
if (!isStringArray$1(autoApprove)) return [name, server];
|
|
46352
46530
|
return [name, {
|
|
46353
46531
|
...rest,
|
|
46354
46532
|
kiroAutoApprove: autoApprove
|
|
@@ -47777,7 +47955,7 @@ function headersRecordToPoolList(headers) {
|
|
|
47777
47955
|
return Object.entries(headers).map(([name, value]) => `${name}: ${value}`);
|
|
47778
47956
|
}
|
|
47779
47957
|
function poolHeadersListToRecord(serverName, headers) {
|
|
47780
|
-
if (!isStringArray$
|
|
47958
|
+
if (!isStringArray$1(headers)) return void 0;
|
|
47781
47959
|
const result = {};
|
|
47782
47960
|
for (const entry of headers) {
|
|
47783
47961
|
const separator = entry.indexOf(":");
|
|
@@ -47803,7 +47981,7 @@ const GLOB_METACHARACTERS = /[*?[]/;
|
|
|
47803
47981
|
/** Patterns named in the glob warning before the rest collapse to `(+N more)`. */
|
|
47804
47982
|
const MAX_LISTED_GLOB_PATTERNS = 10;
|
|
47805
47983
|
function importPoolToolList({ serverName, poolKey, value, warnOnGlobs }) {
|
|
47806
|
-
if (!isStringArray$
|
|
47984
|
+
if (!isStringArray$1(value)) {
|
|
47807
47985
|
warnWithFallback(void 0, `Ignored malformed value for ${poolKey} in Pool MCP server ${quoteValueForWarning(serverName)}: expected a list of strings`);
|
|
47808
47986
|
return;
|
|
47809
47987
|
}
|
|
@@ -48013,7 +48191,7 @@ var PoolMcp = class PoolMcp extends ToolMcp {
|
|
|
48013
48191
|
const poolAllow = isRecord$1(rawServer) ? rawServer.poolAllow : void 0;
|
|
48014
48192
|
return [serverName, {
|
|
48015
48193
|
...serverConfig,
|
|
48016
|
-
...isStringArray$
|
|
48194
|
+
...isStringArray$1(poolAllow) && { poolAllow }
|
|
48017
48195
|
}];
|
|
48018
48196
|
})), logger);
|
|
48019
48197
|
return new PoolMcp({
|
|
@@ -48244,7 +48422,7 @@ function pickServerLists(source) {
|
|
|
48244
48422
|
if (!isPlainObject$1(source)) return {};
|
|
48245
48423
|
return Object.fromEntries(QWENCODE_MCP_SERVER_LIST_KEYS.flatMap((key) => {
|
|
48246
48424
|
const value = source[key];
|
|
48247
|
-
return isStringArray$
|
|
48425
|
+
return isStringArray$1(value) ? [[key, value]] : [];
|
|
48248
48426
|
}));
|
|
48249
48427
|
}
|
|
48250
48428
|
/**
|
|
@@ -48937,7 +49115,7 @@ async function resolveRovodevMcpImportPath({ outputRoot, global, config, logger
|
|
|
48937
49115
|
}
|
|
48938
49116
|
function disabledNamesOf(config) {
|
|
48939
49117
|
const mcpBlock = config && isRecord$1(config.mcp) ? config.mcp : {};
|
|
48940
|
-
return isStringArray$
|
|
49118
|
+
return isStringArray$1(mcpBlock.disabledMcpServers) ? mcpBlock.disabledMcpServers : [];
|
|
48941
49119
|
}
|
|
48942
49120
|
/**
|
|
48943
49121
|
* The value `mcp.mcpConfigPath` needs so Rovo Dev reads the `mcp.json` rulesync
|
|
@@ -49353,7 +49531,7 @@ var RovodevMcp = class RovodevMcp extends ToolMcp {
|
|
|
49353
49531
|
return [];
|
|
49354
49532
|
}
|
|
49355
49533
|
const existingMcp = isRecord$1(existingParsed.mcp) ? { ...existingParsed.mcp } : {};
|
|
49356
|
-
const existingDisabled = isStringArray$
|
|
49534
|
+
const existingDisabled = isStringArray$1(existingMcp.disabledMcpServers) ? existingMcp.disabledMcpServers : [];
|
|
49357
49535
|
const managedNameSet = new Set(managedNames);
|
|
49358
49536
|
const reEnabled = existingDisabled.filter((name) => managedNameSet.has(name) && !disabledNames.includes(name));
|
|
49359
49537
|
if (reEnabled.length > 0) logger?.warn(`Rovo Dev MCP: re-enabling ${reEnabled.join(", ")} — the canonical config does not mark ${reEnabled.length === 1 ? "it" : "them"} disabled. Set "disabled": true in .rulesync/mcp.jsonc to keep a managed server off.`);
|
|
@@ -56512,8 +56690,9 @@ function convertClaudeToRulesyncPermissions(params) {
|
|
|
56512
56690
|
//#endregion
|
|
56513
56691
|
//#region src/features/permissions/cline-permissions.ts
|
|
56514
56692
|
/**
|
|
56515
|
-
* Cline CLI
|
|
56516
|
-
*
|
|
56693
|
+
* The legacy Cline CLI loaded command permissions from the `CLINE_COMMAND_PERMISSIONS`
|
|
56694
|
+
* environment variable. It supported only `allow` and `deny` (no `ask`), and only for
|
|
56695
|
+
* shell commands.
|
|
56517
56696
|
* Schema:
|
|
56518
56697
|
* ```json
|
|
56519
56698
|
* { "allow": ["pattern1", ...], "deny": ["pattern2", ...], "allowRedirects": false }
|
|
@@ -56521,7 +56700,14 @@ function convertClaudeToRulesyncPermissions(params) {
|
|
|
56521
56700
|
*
|
|
56522
56701
|
* Rulesync writes the JSON to `.cline/command-permissions.json` so users can do:
|
|
56523
56702
|
* `export CLINE_COMMAND_PERMISSIONS=$(cat .cline/command-permissions.json)`
|
|
56703
|
+
*
|
|
56704
|
+
* Deprecated: no Cline CLI 3 release reads `CLINE_COMMAND_PERMISSIONS`. Upstream removed
|
|
56705
|
+
* it from the docs in cline/cline@6354d209, and the CLI reference now says the CLI has no
|
|
56706
|
+
* allow or deny list for shell commands and recommends a `PreToolUse` hook instead
|
|
56707
|
+
* (https://docs.cline.bot/cli/cli-reference). The output is still generated for
|
|
56708
|
+
* compatibility, with a deprecation warning on every generation.
|
|
56524
56709
|
*/
|
|
56710
|
+
const CLINE_PERMISSIONS_DEPRECATION_WARNING = "WARNING: The Cline permissions output (.cline/command-permissions.json) is deprecated. No Cline CLI 3 release reads CLINE_COMMAND_PERMISSIONS, so the generated allow/deny lists are not enforced by current Cline releases. To block shell commands in Cline, use a preToolUse hook (rulesync hooks feature, cline target) that cancels the run_commands calls you do not allow. See https://docs.cline.bot/cli/cli-reference.";
|
|
56525
56711
|
const ClineCommandPermissionsSchema = z.looseObject({
|
|
56526
56712
|
allow: z.optional(z.array(z.string())),
|
|
56527
56713
|
deny: z.optional(z.array(z.string())),
|
|
@@ -56639,6 +56825,7 @@ var ClinePermissions = class ClinePermissions extends ToolPermissions {
|
|
|
56639
56825
|
});
|
|
56640
56826
|
}
|
|
56641
56827
|
static async fromRulesyncPermissions({ outputRoot = process.cwd(), rulesyncPermissions, global = false, logger }) {
|
|
56828
|
+
logger?.warn(CLINE_PERMISSIONS_DEPRECATION_WARNING);
|
|
56642
56829
|
const paths = ClinePermissions.getSettablePaths({ global });
|
|
56643
56830
|
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
56644
56831
|
const existingContent = await readFileContentOrNull(filePath) ?? "{}";
|
|
@@ -56668,7 +56855,7 @@ var ClinePermissions = class ClinePermissions extends ToolPermissions {
|
|
|
56668
56855
|
const mergedDeny = uniq([...existing.deny ?? [], ...dedupedDeny]).toSorted();
|
|
56669
56856
|
const denySet = new Set(mergedDeny);
|
|
56670
56857
|
const collisions = dedupedAllow.filter((p) => denySet.has(p));
|
|
56671
|
-
if (collisions.length > 0) logger?.warn(`Cline command permissions: pattern(s) ${collisions.map((p) => `'${p}'`).join(", ")} appear in both 'allow' and 'deny'. Cline
|
|
56858
|
+
if (collisions.length > 0) logger?.warn(`Cline command permissions: pattern(s) ${collisions.map((p) => `'${p}'`).join(", ")} appear in both 'allow' and 'deny'. The legacy Cline CLI gave deny rules precedence, so the 'allow' entry has no effect. Consider removing the duplicate rule.`);
|
|
56672
56859
|
const next = {
|
|
56673
56860
|
...existing,
|
|
56674
56861
|
allow: dedupedAllow,
|
|
@@ -59951,9 +60138,9 @@ function managedContinueToolNames(config) {
|
|
|
59951
60138
|
*/
|
|
59952
60139
|
function parseContinuePermissionLists(parsed) {
|
|
59953
60140
|
return {
|
|
59954
|
-
exclude: isStringArray$
|
|
59955
|
-
ask: isStringArray$
|
|
59956
|
-
allow: isStringArray$
|
|
60141
|
+
exclude: isStringArray$1(parsed.exclude) ? parsed.exclude : [],
|
|
60142
|
+
ask: isStringArray$1(parsed.ask) ? parsed.ask : [],
|
|
60143
|
+
allow: isStringArray$1(parsed.allow) ? parsed.allow : []
|
|
59957
60144
|
};
|
|
59958
60145
|
}
|
|
59959
60146
|
/**
|
|
@@ -60480,8 +60667,8 @@ var CrushPermissions = class CrushPermissions extends ToolPermissions {
|
|
|
60480
60667
|
logger
|
|
60481
60668
|
});
|
|
60482
60669
|
const managedToolNames = managedCrushToolNames(config);
|
|
60483
|
-
const preservedAllowed = (isStringArray$
|
|
60484
|
-
const preservedDisabled = (isStringArray$
|
|
60670
|
+
const preservedAllowed = (isStringArray$1(existingPermissions?.["allowed_tools"]) ? existingPermissions[CRUSH_ALLOWED_TOOLS_KEY] : []).filter((entry) => entry.includes(":") || !managedToolNames.has(entry));
|
|
60671
|
+
const preservedDisabled = (isStringArray$1(existingOptions?.["disabled_tools"]) ? existingOptions[CRUSH_DISABLED_TOOLS_KEY] : []).filter((entry) => !managedToolNames.has(entry));
|
|
60485
60672
|
const allowedList = uniq$1([...preservedAllowed, ...generated.allowed]);
|
|
60486
60673
|
const disabledList = uniq$1([...preservedDisabled, ...generated.disabled]);
|
|
60487
60674
|
const patch = {};
|
|
@@ -60507,8 +60694,8 @@ var CrushPermissions = class CrushPermissions extends ToolPermissions {
|
|
|
60507
60694
|
const permissions = isRecord$1(this.json["permissions"]) ? this.json[CRUSH_PERMISSIONS_KEY] : {};
|
|
60508
60695
|
const options = isRecord$1(this.json["options"]) ? this.json[CRUSH_OPTIONS_KEY] : {};
|
|
60509
60696
|
const rulesyncConfig = convertCrushListsToRulesync({
|
|
60510
|
-
allowed: isStringArray$
|
|
60511
|
-
disabled: isStringArray$
|
|
60697
|
+
allowed: isStringArray$1(permissions["allowed_tools"]) ? permissions[CRUSH_ALLOWED_TOOLS_KEY] : [],
|
|
60698
|
+
disabled: isStringArray$1(options["disabled_tools"]) ? options[CRUSH_DISABLED_TOOLS_KEY] : []
|
|
60512
60699
|
});
|
|
60513
60700
|
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(rulesyncConfig, null, 2) });
|
|
60514
60701
|
}
|
|
@@ -62683,7 +62870,7 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
|
|
|
62683
62870
|
function convertGoosePermissionConfigToRulesync(userPermission) {
|
|
62684
62871
|
const permission = {};
|
|
62685
62872
|
for (const key of GOOSE_PERMISSION_LIST_KEYS) {
|
|
62686
|
-
const toolNames = isStringArray$
|
|
62873
|
+
const toolNames = isStringArray$1(userPermission[key]) ? userPermission[key] : [];
|
|
62687
62874
|
const action = GOOSE_LIST_TO_ACTION[key];
|
|
62688
62875
|
for (const toolName of toolNames) for (const category of GOOSE_TO_RULESYNC_TOOL_NAMES[toolName] ?? [toolName]) {
|
|
62689
62876
|
permission[category] ??= {};
|
|
@@ -63031,7 +63218,7 @@ const ACTION_RANK$3 = {
|
|
|
63031
63218
|
* (mirrors the Cursor adapter's preservation of unmanaged types).
|
|
63032
63219
|
*/
|
|
63033
63220
|
function unmanagedEntries(existingPermission, key) {
|
|
63034
|
-
return (isStringArray$
|
|
63221
|
+
return (isStringArray$1(existingPermission[key]) ? existingPermission[key] : []).filter((entry) => parseGrokEntry(entry) === null);
|
|
63035
63222
|
}
|
|
63036
63223
|
/**
|
|
63037
63224
|
* Bucket canonical rules into Grok's `[permission]` allow/deny/ask arrays of
|
|
@@ -63088,7 +63275,7 @@ function warnRemovedAllToolsRestrictions({ existingPermission, ranked, logger })
|
|
|
63088
63275
|
const generated = ranked.get(GROK_ALL_TOOLS_ENTRY);
|
|
63089
63276
|
for (const action of ["deny", "ask"]) {
|
|
63090
63277
|
const entries = existingPermission[action];
|
|
63091
|
-
if (!isStringArray$
|
|
63278
|
+
if (!isStringArray$1(entries)) continue;
|
|
63092
63279
|
if (!entries.some((entry) => parseGrokEntry(entry)?.category === "*")) continue;
|
|
63093
63280
|
if (generated !== void 0 && ACTION_RANK$3[generated] >= ACTION_RANK$3[action]) continue;
|
|
63094
63281
|
logger.warn(`Grok CLI permissions: removing the existing bare '*' entry from the '${action}' array — Rulesync now manages it through the all-tools '*' category, and the canonical config has no '*' '${action}' rule. Add "*": { "*": "${action}" } to keep restricting every tool.`);
|
|
@@ -63119,9 +63306,9 @@ function warnEntriesShadowedByAllTools({ ranked, logger }) {
|
|
|
63119
63306
|
* described more than once resolves to the strictest action.
|
|
63120
63307
|
*/
|
|
63121
63308
|
function parseGrokPermissionArrays(permission) {
|
|
63122
|
-
const allow = isStringArray$
|
|
63123
|
-
const deny = isStringArray$
|
|
63124
|
-
const ask = isStringArray$
|
|
63309
|
+
const allow = isStringArray$1(permission.allow) ? permission.allow : void 0;
|
|
63310
|
+
const deny = isStringArray$1(permission.deny) ? permission.deny : void 0;
|
|
63311
|
+
const ask = isStringArray$1(permission.ask) ? permission.ask : void 0;
|
|
63125
63312
|
const rules = Array.isArray(permission[GROKCLI_PERMISSION_RULES_KEY]) ? permission[GROKCLI_PERMISSION_RULES_KEY] : void 0;
|
|
63126
63313
|
if ((allow?.length ?? 0) + (deny?.length ?? 0) + (ask?.length ?? 0) + (rules?.length ?? 0) === 0) return null;
|
|
63127
63314
|
const result = {};
|
|
@@ -63329,20 +63516,20 @@ var HermesagentPermissions = class HermesagentPermissions extends ToolPermission
|
|
|
63329
63516
|
const permission = clonePermissionBlock(provenance.permission);
|
|
63330
63517
|
reconcileCommandAllowlist({
|
|
63331
63518
|
permission,
|
|
63332
|
-
commandAllowlist: isStringArray$
|
|
63519
|
+
commandAllowlist: isStringArray$1(config.command_allowlist) ? config.command_allowlist : []
|
|
63333
63520
|
});
|
|
63334
63521
|
const approvals = isRecord$1(config.approvals) ? config.approvals : {};
|
|
63335
63522
|
reconcileNativeDenies({
|
|
63336
63523
|
permission,
|
|
63337
63524
|
category: SHELL_PERMISSION_CATEGORY,
|
|
63338
|
-
patterns: isStringArray$
|
|
63525
|
+
patterns: isStringArray$1(approvals.deny) ? approvals.deny : []
|
|
63339
63526
|
});
|
|
63340
63527
|
const security = isRecord$1(config.security) ? config.security : {};
|
|
63341
63528
|
const websiteBlocklist = isRecord$1(security.website_blocklist) ? security.website_blocklist : {};
|
|
63342
63529
|
reconcileNativeDenies({
|
|
63343
63530
|
permission,
|
|
63344
63531
|
category: WEBFETCH_PERMISSION_CATEGORY,
|
|
63345
|
-
patterns: websiteBlocklist.enabled === true && isStringArray$
|
|
63532
|
+
patterns: websiteBlocklist.enabled === true && isStringArray$1(websiteBlocklist.domains) ? websiteBlocklist.domains : []
|
|
63346
63533
|
});
|
|
63347
63534
|
removeEmptyCategories(permission);
|
|
63348
63535
|
const { permission: _permission, hermes: _hermes, ...otherProvenance } = provenance;
|
|
@@ -64736,13 +64923,13 @@ function classifyExistingEntry({ emitted, capability, pattern, effect }) {
|
|
|
64736
64923
|
* how many entries rulesync does not generate.
|
|
64737
64924
|
*/
|
|
64738
64925
|
function mergeExistingRule({ rule, emitted, logger }) {
|
|
64739
|
-
if (!isRecord$1(rule) || typeof rule.capability !== "string" || rule.exclude !== void 0 || !MANAGED_CAPABILITIES.has(rule.capability) || rule.match !== void 0 && !isStringArray$
|
|
64926
|
+
if (!isRecord$1(rule) || typeof rule.capability !== "string" || rule.exclude !== void 0 || !MANAGED_CAPABILITIES.has(rule.capability) || rule.match !== void 0 && !isStringArray$1(rule.match)) return {
|
|
64740
64927
|
rule,
|
|
64741
64928
|
unrelatedEntries: 0
|
|
64742
64929
|
};
|
|
64743
64930
|
const capability = rule.capability;
|
|
64744
64931
|
const isCatchAll = rule.match === void 0;
|
|
64745
|
-
const patterns = isStringArray$
|
|
64932
|
+
const patterns = isStringArray$1(rule.match) ? rule.match : [CATCH_ALL_PATTERN$6];
|
|
64746
64933
|
if (patterns.length === 0) return {
|
|
64747
64934
|
rule,
|
|
64748
64935
|
unrelatedEntries: 0
|
|
@@ -64926,7 +65113,7 @@ var KiroCapabilityPermissions = class KiroCapabilityPermissions extends ToolPerm
|
|
|
64926
65113
|
for (const rule of rules) {
|
|
64927
65114
|
if (!isImportableRule(rule) || !isRecord$1(rule) || !isPermissionAction$1(rule.effect)) continue;
|
|
64928
65115
|
const category = KIRO_CAPABILITY_TO_CANONICAL[rule.capability];
|
|
64929
|
-
const patterns = rule.match === void 0 ? [CATCH_ALL_PATTERN$6] : isStringArray$
|
|
65116
|
+
const patterns = rule.match === void 0 ? [CATCH_ALL_PATTERN$6] : isStringArray$1(rule.match) ? rule.match : [];
|
|
64930
65117
|
const categoryRules = permission[category] ??= {};
|
|
64931
65118
|
for (const pattern of patterns) {
|
|
64932
65119
|
const previous = categoryRules[pattern];
|
|
@@ -66644,8 +66831,8 @@ function importPoolTool({ toolConfig, bucket }) {
|
|
|
66644
66831
|
bucket[CATCH_ALL_PATTERN$3] = "deny";
|
|
66645
66832
|
return;
|
|
66646
66833
|
}
|
|
66647
|
-
const denied = isStringArray$
|
|
66648
|
-
const allowed = isStringArray$
|
|
66834
|
+
const denied = isStringArray$1(toolConfig[POOL_DENY_KEY]) ? toolConfig[POOL_DENY_KEY] : [];
|
|
66835
|
+
const allowed = isStringArray$1(toolConfig[POOL_ALLOW_KEY]) ? toolConfig[POOL_ALLOW_KEY] : [];
|
|
66649
66836
|
for (const pattern of denied.filter(isUsablePattern)) bucket[pattern] = "deny";
|
|
66650
66837
|
for (const pattern of allowed.filter(isUsablePattern)) bucket[pattern] ??= "allow";
|
|
66651
66838
|
}
|
|
@@ -68850,7 +69037,7 @@ function convertRovodevToolPermissionsToRulesync(toolPermissions) {
|
|
|
68850
69037
|
permission[category] ??= {};
|
|
68851
69038
|
permission[category][CATCH_ALL_PATTERN$1] = levels.reduce((strictest, level) => strictestAction(strictest, level ?? implicitLevel), permission[category][CATCH_ALL_PATTERN$1]);
|
|
68852
69039
|
}
|
|
68853
|
-
if (isStringArray$
|
|
69040
|
+
if (isStringArray$1(toolPermissions.allowedExternalPaths)) for (const path of toolPermissions.allowedExternalPaths) {
|
|
68854
69041
|
permission.read ??= {};
|
|
68855
69042
|
permission.read[path] ??= "allow";
|
|
68856
69043
|
}
|
|
@@ -69106,7 +69293,7 @@ function stripRefusedOverridePaths(override) {
|
|
|
69106
69293
|
*/
|
|
69107
69294
|
function collectTrustAffectingOverrideEntries({ tools, general }) {
|
|
69108
69295
|
const entries = [];
|
|
69109
|
-
const allowed = isStringArray$
|
|
69296
|
+
const allowed = isStringArray$1(tools[ALLOWED_KEY]) ? tools[ALLOWED_KEY] : [];
|
|
69110
69297
|
if (allowed.length > 0) entries.push({
|
|
69111
69298
|
label: `tools.allowed (${allowed.map(quoteValueForWarning).join(", ")})`,
|
|
69112
69299
|
reason: "auto-approves what it names as the override spells it; a non-shell entry is only checked against a whole-tool deny of its tool"
|
|
@@ -69257,7 +69444,7 @@ function withholdDeniedOverrideAllowed({ overrideVerbatimAllowed, exclude, logge
|
|
|
69257
69444
|
* hand-written entry went.
|
|
69258
69445
|
*/
|
|
69259
69446
|
function reportDroppedManagedEntries({ existingTools, allowedList, excludeList, logger }) {
|
|
69260
|
-
const dropped = (key, list) => uniq(isStringArray$
|
|
69447
|
+
const dropped = (key, list) => uniq(isStringArray$1(existingTools?.[key]) ? existingTools[key] : []).filter((entry) => {
|
|
69261
69448
|
if (list.includes(entry)) return false;
|
|
69262
69449
|
const toolName = parseTabnineEntry(entry)?.toolName;
|
|
69263
69450
|
return toolName === void 0 || !list.includes(toolName);
|
|
@@ -69339,7 +69526,7 @@ var TabninePermissions = class TabninePermissions extends ToolPermissions {
|
|
|
69339
69526
|
}
|
|
69340
69527
|
const overrideList = (key) => {
|
|
69341
69528
|
const value = overrideTools[key];
|
|
69342
|
-
if (value === void 0 || isStringArray$
|
|
69529
|
+
if (value === void 0 || isStringArray$1(value)) return value ?? [];
|
|
69343
69530
|
warnWithFallback(logger, `Tabnine CLI permissions: ignored tools.${key} of the tabnine override (${quoteValueForWarning(value)}); it must be a list of tool names.`);
|
|
69344
69531
|
return [];
|
|
69345
69532
|
};
|
|
@@ -69377,7 +69564,7 @@ var TabninePermissions = class TabninePermissions extends ToolPermissions {
|
|
|
69377
69564
|
if (ignoredOverrideKeys.length > 0) warnWithFallback(logger, `Tabnine CLI permissions: ignored ${ignoredOverrideKeys.length} key(s) of the tabnine override (${ignoredOverrideKeys.map(quoteValueForWarning).join(", ")}); only 'tools' and 'general' are written through the permissions feature.`);
|
|
69378
69565
|
const existingTools = this.existingToolsGroup(existingContent);
|
|
69379
69566
|
const managedToolNames = new Set(Object.keys(config.permission).map((category) => category === "bash" || category === "*" ? SHELL_TOOL_NAME : toTabnineToolName(category)));
|
|
69380
|
-
const preservedEntries = (key) => (isStringArray$
|
|
69567
|
+
const preservedEntries = (key) => (isStringArray$1(existingTools?.[key]) ? existingTools[key] : []).filter((entry) => {
|
|
69381
69568
|
const toolName = parseTabnineEntry(entry)?.toolName;
|
|
69382
69569
|
return toolName === void 0 || !managedToolNames.has(toolName);
|
|
69383
69570
|
});
|
|
@@ -69455,7 +69642,7 @@ var TabninePermissions = class TabninePermissions extends ToolPermissions {
|
|
|
69455
69642
|
if (rules !== void 0 && !Object.hasOwn(rules, pattern)) rules[pattern] = action;
|
|
69456
69643
|
};
|
|
69457
69644
|
for (const [key, action] of [[EXCLUDE_KEY, "deny"], [ALLOWED_KEY, "allow"]]) {
|
|
69458
|
-
const entries = isStringArray$
|
|
69645
|
+
const entries = isStringArray$1(tools[key]) ? tools[key] : [];
|
|
69459
69646
|
for (const entry of entries) {
|
|
69460
69647
|
const parsed = parseTabnineEntry(entry);
|
|
69461
69648
|
if (parsed === void 0) {
|
|
@@ -70323,12 +70510,12 @@ const WIDE_CHARACTERS_PATTERN = /\p{Emoji_Presentation}|\p{Emoji_Modifier_Base}|
|
|
|
70323
70510
|
* The ranges are the `East_Asian_Width=A` property of Unicode 17.0, taken from
|
|
70324
70511
|
* the table `get-east-asian-width` 1.6.0 carries and merged where two are
|
|
70325
70512
|
* adjacent. The table is written out here rather than read from the package so
|
|
70326
|
-
* that the one lookup adds no dependency of its own, and the drift test
|
|
70327
|
-
* `display-width.test.ts` is what keeps it honest: it loads the package
|
|
70328
|
-
*
|
|
70329
|
-
*
|
|
70330
|
-
*
|
|
70331
|
-
* the
|
|
70513
|
+
* that the one lookup adds no runtime dependency of its own, and the drift test
|
|
70514
|
+
* in `display-width.test.ts` is what keeps it honest: it loads the package,
|
|
70515
|
+
* pinned as a dev dependency, walks every code point, and fails when this
|
|
70516
|
+
* table and the property disagree in either direction. When that pin moves to
|
|
70517
|
+
* a newer Unicode, that test is what fails, and the table is regenerated from
|
|
70518
|
+
* the new property — its ranges, marks aside,
|
|
70332
70519
|
* merged where adjacent and written as escapes — with the version named here.
|
|
70333
70520
|
*
|
|
70334
70521
|
* Three stretches of the property are left out because they never reach this
|
|
@@ -72077,8 +72264,8 @@ var WarpPermissions = class WarpPermissions extends ToolPermissions {
|
|
|
72077
72264
|
const executionProfiles = isRecord$1(agents["execution_profiles"]) ? agents[EXECUTION_PROFILES_KEY] : void 0;
|
|
72078
72265
|
const defaultProfile = executionProfiles && isRecord$1(executionProfiles["default"]) ? executionProfiles[DEFAULT_PROFILE_KEY] : void 0;
|
|
72079
72266
|
const config = convertWarpToRulesyncPermissions({
|
|
72080
|
-
allow: defaultProfile ? isStringArray$
|
|
72081
|
-
deny: defaultProfile ? isStringArray$
|
|
72267
|
+
allow: defaultProfile ? isStringArray$1(defaultProfile["command_allowlist"]) ? defaultProfile[PROFILE_ALLOWLIST_KEY] : [] : isStringArray$1(profiles[ALLOWLIST_KEY]) ? profiles[ALLOWLIST_KEY] : [],
|
|
72268
|
+
deny: defaultProfile ? isStringArray$1(defaultProfile["command_denylist"]) ? defaultProfile[PROFILE_DENYLIST_KEY] : [] : isStringArray$1(profiles[DENYLIST_KEY]) ? profiles[DENYLIST_KEY] : []
|
|
72082
72269
|
});
|
|
72083
72270
|
const warpOverride = {};
|
|
72084
72271
|
for (const key of WARP_OVERRIDE_KEYS) if (profiles[key] !== void 0) warpOverride[key] = profiles[key];
|
|
@@ -72608,8 +72795,8 @@ var WarpcliPermissions = class WarpcliPermissions extends ToolPermissions {
|
|
|
72608
72795
|
const allowlist = defaultProfile[PROFILE_ALLOWLIST_KEY];
|
|
72609
72796
|
const denylist = defaultProfile[PROFILE_DENYLIST_KEY];
|
|
72610
72797
|
const config = convertWarpToRulesyncPermissions({
|
|
72611
|
-
allow: isStringArray$
|
|
72612
|
-
deny: isStringArray$
|
|
72798
|
+
allow: isStringArray$1(allowlist) ? allowlist : [],
|
|
72799
|
+
deny: isStringArray$1(denylist) ? denylist : []
|
|
72613
72800
|
});
|
|
72614
72801
|
const executionProfileOverride = liftExecutionProfileOverride(defaultProfile);
|
|
72615
72802
|
const warpcliOverride = {};
|
|
@@ -84624,7 +84811,7 @@ var BobSubagent = class extends RooSubagent {
|
|
|
84624
84811
|
const mode = super.toRooMode(rulesyncSubagent);
|
|
84625
84812
|
const frontmatter = rulesyncSubagent.getFrontmatter();
|
|
84626
84813
|
const bobSection = isRecord$1(frontmatter.bob) ? frontmatter.bob : {};
|
|
84627
|
-
if (isStringArray$
|
|
84814
|
+
if (isStringArray$1(bobSection.allowedSubagents)) mode.allowedSubagents = bobSection.allowedSubagents;
|
|
84628
84815
|
return mode;
|
|
84629
84816
|
}
|
|
84630
84817
|
};
|
|
@@ -90079,7 +90266,7 @@ var ZoocodeSubagent = class extends RooSubagent {
|
|
|
90079
90266
|
const mode = super.toRooMode(rulesyncSubagent);
|
|
90080
90267
|
const frontmatter = rulesyncSubagent.getFrontmatter();
|
|
90081
90268
|
const zoocodeSection = isRecord$1(frontmatter.zoocode) ? frontmatter.zoocode : {};
|
|
90082
|
-
if (isStringArray$
|
|
90269
|
+
if (isStringArray$1(zoocodeSection.allowedMcpServers)) mode.allowedMcpServers = zoocodeSection.allowedMcpServers;
|
|
90083
90270
|
return mode;
|
|
90084
90271
|
}
|
|
90085
90272
|
toRulesyncSubagents() {
|
|
@@ -90091,7 +90278,7 @@ var ZoocodeSubagent = class extends RooSubagent {
|
|
|
90091
90278
|
targets: ["zoocode"],
|
|
90092
90279
|
roo: restRooSection
|
|
90093
90280
|
};
|
|
90094
|
-
if (isStringArray$
|
|
90281
|
+
if (isStringArray$1(allowedMcpServers)) rebuilt.zoocode = { allowedMcpServers };
|
|
90095
90282
|
return new RulesyncSubagent({
|
|
90096
90283
|
outputRoot: ".",
|
|
90097
90284
|
frontmatter: rebuilt,
|
|
@@ -94032,7 +94219,8 @@ var NestedAgentsmdRule = class extends ToolRule {
|
|
|
94032
94219
|
* `<subprojectPath>/AGENTS.md` (project scope only); every other non-root rule
|
|
94033
94220
|
* folds into the root `AGENTS.md`, since Codex has no modular rules directory.
|
|
94034
94221
|
* The project files on the root-to-cwd chain share one `project_doc_max_bytes`
|
|
94035
|
-
* budget (32 KiB by default
|
|
94222
|
+
* budget (32 KiB by default, or the value set in the project's
|
|
94223
|
+
* `.codex/config.toml`), which the RulesProcessor warns about.
|
|
94036
94224
|
*
|
|
94037
94225
|
* @see https://learn.chatgpt.com/docs/agent-configuration/agents-md
|
|
94038
94226
|
*/
|
|
@@ -94044,6 +94232,29 @@ var CodexcliRule = class extends NestedAgentsmdRule {
|
|
|
94044
94232
|
};
|
|
94045
94233
|
}
|
|
94046
94234
|
};
|
|
94235
|
+
/**
|
|
94236
|
+
* Read the `project_doc_max_bytes` value set in the project's
|
|
94237
|
+
* `.codex/config.toml`, which Codex loads for trusted projects and which
|
|
94238
|
+
* overrides the 32 KiB default budget for project `AGENTS.md` files.
|
|
94239
|
+
*
|
|
94240
|
+
* Answers `undefined` when the file is missing, is not valid TOML, or does not
|
|
94241
|
+
* set the key to a positive integer, so the caller falls back to the default.
|
|
94242
|
+
*
|
|
94243
|
+
* @see https://learn.chatgpt.com/docs/config-file/config-reference
|
|
94244
|
+
*/
|
|
94245
|
+
async function readCodexcliProjectDocMaxBytes(outputRoot) {
|
|
94246
|
+
const content = await readFileContentOrNull(join(outputRoot, CODEXCLI_DIR, CODEXCLI_MCP_FILE_NAME));
|
|
94247
|
+
if (content === null) return;
|
|
94248
|
+
let parsed;
|
|
94249
|
+
try {
|
|
94250
|
+
parsed = smolToml.parse(content);
|
|
94251
|
+
} catch {
|
|
94252
|
+
return;
|
|
94253
|
+
}
|
|
94254
|
+
if (!isPlainObject$1(parsed)) return;
|
|
94255
|
+
const value = parsed.project_doc_max_bytes;
|
|
94256
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : void 0;
|
|
94257
|
+
}
|
|
94047
94258
|
//#endregion
|
|
94048
94259
|
//#region src/features/rules/commandcode-rule.ts
|
|
94049
94260
|
var CommandcodeRule = class CommandcodeRule extends ToolRule {
|
|
@@ -99418,7 +99629,9 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
99418
99629
|
collisionPolicy: "fold",
|
|
99419
99630
|
projectInstructionBudget: {
|
|
99420
99631
|
bytes: CODEXCLI_PROJECT_DOC_MAX_BYTES,
|
|
99421
|
-
remedy: "Codex CLI stops reading project `AGENTS.md` files once their combined size on the root-to-working-directory chain reaches `project_doc_max_bytes` (32 KiB by default) and silently drops the rest. Move directory-scoped rules into nested `AGENTS.md` files with `agentsmd.subprojectPath` (or `deriveSubprojectPathFromGlobs: true` in rulesync.jsonc), trim the rules, or raise `project_doc_max_bytes` in `.codex/config.toml
|
|
99632
|
+
remedy: "Codex CLI stops reading project `AGENTS.md` files once their combined size on the root-to-working-directory chain reaches `project_doc_max_bytes` (32 KiB by default) and silently drops the rest. Move directory-scoped rules into nested `AGENTS.md` files with `agentsmd.subprojectPath` (or `deriveSubprojectPathFromGlobs: true` in rulesync.jsonc), trim the rules, or raise `project_doc_max_bytes` in `.codex/config.toml`.",
|
|
99633
|
+
readConfiguredBytes: readCodexcliProjectDocMaxBytes,
|
|
99634
|
+
configuredSource: "`project_doc_max_bytes` in `.codex/config.toml`"
|
|
99422
99635
|
}
|
|
99423
99636
|
}
|
|
99424
99637
|
}],
|
|
@@ -99963,7 +100176,7 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
99963
100176
|
factory
|
|
99964
100177
|
});
|
|
99965
100178
|
await this.warnForWarpLegacyRootFile(toolRules);
|
|
99966
|
-
this.warnForInstructionBudget({
|
|
100179
|
+
await this.warnForInstructionBudget({
|
|
99967
100180
|
toolRules,
|
|
99968
100181
|
meta
|
|
99969
100182
|
});
|
|
@@ -99984,14 +100197,19 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
99984
100197
|
* once, at its shallowest directory. With several output roots the warning
|
|
99985
100198
|
* names the root it refers to. An output root below the git root is measured
|
|
99986
100199
|
* without the parent directories' files Codex would also load, so the
|
|
99987
|
-
* estimate can come out low there. The budget checked is the
|
|
99988
|
-
*
|
|
100200
|
+
* estimate can come out low there. The budget checked is the value set in the
|
|
100201
|
+
* output root's tool config when the tool supports one (Codex's
|
|
100202
|
+
* `project_doc_max_bytes` in `.codex/config.toml`), else the tool's default;
|
|
100203
|
+
* the warning names which one it used. Global scope is not
|
|
99989
100204
|
* checked because Codex loads the personal `~/.codex/AGENTS.md` outside the
|
|
99990
100205
|
* project budget.
|
|
99991
100206
|
*/
|
|
99992
|
-
warnForInstructionBudget({ toolRules, meta }) {
|
|
100207
|
+
async warnForInstructionBudget({ toolRules, meta }) {
|
|
99993
100208
|
const budget = meta.projectInstructionBudget;
|
|
99994
100209
|
if (budget === void 0 || this.global) return;
|
|
100210
|
+
const configuredBytes = await budget.readConfiguredBytes?.(this.outputRoot);
|
|
100211
|
+
const limitBytes = configuredBytes ?? budget.bytes;
|
|
100212
|
+
const limitLabel = budget.readConfiguredBytes === void 0 ? `${limitBytes}-byte limit` : configuredBytes === void 0 ? `${limitBytes}-byte default limit` : `${limitBytes}-byte limit set by ${budget.configuredSource ?? "the tool config"}`;
|
|
99995
100213
|
const files = toolRules.map((rule) => ({
|
|
99996
100214
|
dir: normalizeRelativeDir(rule.getRelativeDirPath()),
|
|
99997
100215
|
path: toPosixPath(join(rule.getRelativeDirPath(), rule.getRelativeFilePath())),
|
|
@@ -100001,8 +100219,8 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
100001
100219
|
const location = this.outputRoot === process.cwd() ? "" : ` under '${this.outputRoot}'`;
|
|
100002
100220
|
const rootFiles = files.filter(({ dir }) => dir === ".");
|
|
100003
100221
|
const rootBytes = rootFiles.reduce((sum, { bytes }) => sum + bytes, 0);
|
|
100004
|
-
if (rootBytes >
|
|
100005
|
-
this.logger.warn(`The generated ${this.toolTarget} root instruction file${location} (${rootFiles.map(({ path }) => path).join(", ")}) is ${rootBytes} bytes, over the ${
|
|
100222
|
+
if (rootBytes > limitBytes) {
|
|
100223
|
+
this.logger.warn(`The generated ${this.toolTarget} root instruction file${location} (${rootFiles.map(({ path }) => path).join(", ")}) is ${rootBytes} bytes, over the ${limitLabel}. ${budget.remedy}`);
|
|
100006
100224
|
return;
|
|
100007
100225
|
}
|
|
100008
100226
|
const reportedDirs = [];
|
|
@@ -100011,9 +100229,9 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
100011
100229
|
if (reportedDirs.some((dir) => isAncestorOrSelf(dir, file.dir))) continue;
|
|
100012
100230
|
const chain = files.filter(({ dir }) => isAncestorOrSelf(dir, file.dir));
|
|
100013
100231
|
const chainBytes = chain.reduce((sum, { bytes }) => sum + bytes, 0);
|
|
100014
|
-
if (chainBytes >
|
|
100232
|
+
if (chainBytes > limitBytes) {
|
|
100015
100233
|
reportedDirs.push(file.dir);
|
|
100016
|
-
this.logger.warn(`The generated ${this.toolTarget} instruction files${location} loaded while working in '${file.dir}' (${chain.map(({ path }) => path).join(", ")}) total ${chainBytes} bytes, over the ${
|
|
100234
|
+
this.logger.warn(`The generated ${this.toolTarget} instruction files${location} loaded while working in '${file.dir}' (${chain.map(({ path }) => path).join(", ")}) total ${chainBytes} bytes, over the ${limitLabel} (as is every directory below it). ${budget.remedy}`);
|
|
100017
100235
|
}
|
|
100018
100236
|
}
|
|
100019
100237
|
}
|
|
@@ -103918,4 +104136,4 @@ async function importChecksCore(params) {
|
|
|
103918
104136
|
//#endregion
|
|
103919
104137
|
export { RulesyncHooks as $, removeDirectoryStrict as $t, groupSpellingsByCaseFoldedIdentity as A, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as An, applyFileMode as At, QWENCODE_LOCAL_RULE_FILE_NAME as B, parseCommaSeparatedList as Bn, getFileSizeAndExecutableBit as Bt, COMMANDCODE_SETTINGS_LOCAL_FILE_NAME as C, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as Cn, fallbackLogger as Ct, FACTORYDROID_DIR as D, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as Dn, withWarnOnceScope as Dt, ChecksProcessor as E, RULESYNC_IGNORE_RELATIVE_FILE_PATH as En, resetRunWarningState as Et, CLAUDECODE_LOCAL_RULE_FILE_NAME as F, RULESYNC_SKILLS_RELATIVE_DIR_PATH as Fn, createTempDirectory as Ft, RulesyncSubagentFrontmatterSchema as G, truncateText as Gn, listDirectoryEntryNames as Gt, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as H, ALL_FEATURES_WITH_WILDCARD as Hn, isFileNotFoundError as Ht, CLAUDECODE_MEMORIES_DIR_NAME as I, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as In, directoryExists as It, RulesyncRule as J, quoteForLog as Jn, pathEscapesRoot as Jt, RulesyncSkill as K, hasDeceptiveHiddenCharacters as Kn, listFilePathsRecursively as Kt, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as L, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as Ln, ensureDir as Lt, CODEXCLI_DIR as M, RULESYNC_PERMISSIONS_SCHEMA_URL as Mn, assertTreeContainsNoSymlinks as Mt, CLAUDECODE_AGENT_MEMORY_LOCAL_DIR_NAME as N, RULESYNC_RELATIVE_DIR_PATH as Nn, assertWritablePathInsideRoot as Nt, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as O, RULESYNC_MCP_RELATIVE_FILE_PATH as On, CLIError as Ot, CLAUDECODE_DIR as P, RULESYNC_RULES_RELATIVE_DIR_PATH as Pn, checkPathTraversal as Pt, RulesyncIgnore as Q, removeDirectory as Qt, CLAUDECODE_SKILLS_DIR_PATH as R, RULESYNC_USER_CONFIG_DIR_NAME as Rn, fileExists as Rt, COMMANDCODE_DIR as S, RULESYNC_CONFIG_SCHEMA_URL as Sn, WarningCollectingLogger as St, CODEBUDDY_LOCAL_RULE_FILE_NAME as T, RULESYNC_HOOKS_RELATIVE_FILE_PATH as Tn, withFallbackLoggerTarget as Tt, getLocalSkillDirNames as U, DEPRECATED_FEATURE_REPLACEMENTS as Un, isFileSystemError as Ut, AUGMENTCODE_DIR as V, ALL_FEATURES as Vn, getHomeDirectory as Vt, RulesyncSubagent as W, formatError as Wn, isSymlink as Wt, RulesyncPermissions as X, stripControlCharactersKeepingLineFeeds as Xn, readFileContent as Xt, RulesyncRuleFrontmatterSchema as Y, stripControlCharacters as Yn, posixRelativePathEscapesRoot as Yt, RulesyncMcp as Z, stripHiddenCharacters as Zn, readFileContentOrNull as Zt, LETTACODE_DIR as _, MAX_FILE_SIZE as _n, SourceEntrySchema as _t, getProcessorRegistryEntry as a, runWithDirectoryRollback as an, RulesyncCheck as at, CommandsProcessor as b, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as bn, ConsoleLogger as bt, RulesProcessor as c, writeFileBuffer as cn, loadYaml as ct, ELLIPSIS_WIDTH as d, ALL_TOOL_TARGETS as dn, ConfigResolver as dt, removeFile as en, getRulesyncSourceCandidates as et, displayWidthOf as f, ALL_TOOL_TARGETS_WITH_WILDCARD as fn, mergeInputRootConfigs as ft, HooksProcessor as g, FEATURE_SOURCE_TREE_ENTRIES as gn, GITIGNORE_DESTINATION_KEY as gt, IgnoreProcessor as h, CURATED_RULES_FEATURE_SUBDIR as hn, ConfigFileSchema as ht, inspectInputRoots as i, resolvedRelativePath as in, RulesyncCommandFrontmatterSchema as it, CODEXCLI_BASH_RULES_FILE_NAME as j, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as jn, assertDirectoryIfExists as jt, caseFoldIdentity as k, RULESYNC_MCP_SCHEMA_URL as kn, ErrorCodes as kt, SubagentsProcessor as l, writeFileContent as ln, SHARED_USER_MANAGED_CONFIG_PATHS as lt, McpProcessor as m, ToolTargetSchema as mn, CONFLICTING_TARGET_PAIRS as mt, formatSourceLoadFailure as n, removeTempDirectory as nn, parseJsonc as nt, convertFromTool as o, splitPathSegments as on, RulesyncCheckFrontmatterSchema as ot, shortenToWidth as p, PACKAGING_TOOL_TARGETS as pn, resolveEffectiveInputRoots as pt, RulesyncSkillFrontmatterSchema as q, hasEnclosingMarkOutsideKeycap as qn, listSubdirectoryNames as qt, generate as r, resolvePath as rn, RulesyncCommand as rt, isPackagingToolTarget as s, toPosixPath as sn, stringifyFrontmatter as st, importFromTool as t, removeFileStrict as tn, resolveRulesyncSourceWritePath as tt, SkillsProcessor as u, mapWithConcurrency as un, SKILL_FILE_NAME as ut, LETTACODE_SETTINGS_LOCAL_FILE_NAME as v, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as vn, assertTargetsFeaturesExclusive as vt, CODEBUDDY_DIR as w, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as wn, warnOnConflictingFlags as wt, REASONIX_LOCAL_RULE_FILE_NAME as x, RULESYNC_CONFIG_RELATIVE_FILE_PATH as xn, JsonLogger as xt, CRUSH_LOCAL_RULE_FILE_NAME as y, RULESYNC_CHECKS_RELATIVE_DIR_PATH as yn, findControlCharacter as yt, QWENCODE_DIR as z, RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME as zn, findFilesByGlobs as zt };
|
|
103920
104138
|
|
|
103921
|
-
//# sourceMappingURL=import-
|
|
104139
|
+
//# sourceMappingURL=import-BPSwSahy.js.map
|