rulesync 36.0.2 → 37.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.cjs +83 -26
- package/dist/cli/index.js +83 -26
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-Tya9qZRK.js → import-BsEFYJsg.js} +776 -129
- package/dist/{import-Tya9qZRK.js.map → import-BsEFYJsg.js.map} +1 -1
- package/dist/{import-DVcemXlR.cjs → import-DYYG88Rs.cjs} +792 -145
- 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
|
|
@@ -7895,14 +7895,53 @@ const COPILOT_DEFAULT_PERMISSION_MODES = [
|
|
|
7895
7895
|
"autoApprove",
|
|
7896
7896
|
"autopilot"
|
|
7897
7897
|
];
|
|
7898
|
+
/** `chat.agent.sandbox.enabled`: `"off"` or `"on"`. */
|
|
7899
|
+
const CopilotSandboxEnabledSchema = z.enum(["off", "on"]);
|
|
7900
|
+
/** `chat.agent.sandbox.fileSystem.userConfiguredPaths`. */
|
|
7901
|
+
const CopilotSandboxUserConfiguredPathsSchema = z.strictObject({
|
|
7902
|
+
readwritePaths: z.optional(z.array(z.string())),
|
|
7903
|
+
readonlyPaths: z.optional(z.array(z.string())),
|
|
7904
|
+
deniedPaths: z.optional(z.array(z.string()))
|
|
7905
|
+
});
|
|
7906
|
+
/**
|
|
7907
|
+
* The Agent Host sandbox settings VS Code 1.141 settled on, as a nested object
|
|
7908
|
+
* whose path mirrors the dotted setting key under `chat.agent.sandbox.` — e.g.
|
|
7909
|
+
* `network.allowNetwork` writes `chat.agent.sandbox.network.allowNetwork`.
|
|
7910
|
+
* Only the documented, registered keys are accepted; the deprecated 1.138
|
|
7911
|
+
* shapes (`chat.agent.sandbox.allowNetwork`, `.fileSystem.linux|mac|windows`)
|
|
7912
|
+
* are not modeled, since the Agent Host ignores them.
|
|
7913
|
+
*
|
|
7914
|
+
* @see https://code.visualstudio.com/docs/agents/run/agent-sandboxing
|
|
7915
|
+
* @see https://code.visualstudio.com/docs/agents/reference/ai-settings
|
|
7916
|
+
*/
|
|
7917
|
+
const CopilotSandboxSchema = z.strictObject({
|
|
7918
|
+
enabled: z.optional(CopilotSandboxEnabledSchema),
|
|
7919
|
+
network: z.optional(z.strictObject({
|
|
7920
|
+
allowNetwork: z.optional(z.boolean()),
|
|
7921
|
+
allowLocalNetwork: z.optional(z.boolean())
|
|
7922
|
+
})),
|
|
7923
|
+
allowUnsandboxedCommands: z.optional(z.boolean()),
|
|
7924
|
+
mcpServers: z.optional(z.boolean()),
|
|
7925
|
+
lspServers: z.optional(z.boolean()),
|
|
7926
|
+
credentials: z.optional(z.strictObject({
|
|
7927
|
+
authenticategit: z.optional(z.boolean()),
|
|
7928
|
+
authenticategh: z.optional(z.boolean())
|
|
7929
|
+
})),
|
|
7930
|
+
fileSystem: z.optional(z.strictObject({
|
|
7931
|
+
allowDevToolAccess: z.optional(z.boolean()),
|
|
7932
|
+
userConfiguredPaths: z.optional(CopilotSandboxUserConfiguredPathsSchema)
|
|
7933
|
+
}))
|
|
7934
|
+
});
|
|
7898
7935
|
/**
|
|
7899
7936
|
* Tool-scoped override block for GitHub Copilot in VS Code.
|
|
7900
7937
|
* `defaultPermissionMode` writes the workspace `chat.permissions.default`
|
|
7901
|
-
* setting,
|
|
7938
|
+
* setting, and `sandbox` writes the `chat.agent.sandbox.*` settings; neither
|
|
7939
|
+
* has a canonical category.
|
|
7902
7940
|
*/
|
|
7903
7941
|
const CopilotPermissionsOverrideSchema = z.looseObject({
|
|
7904
7942
|
permission: z.optional(ToolScopedPermissionSchema),
|
|
7905
|
-
defaultPermissionMode: z.optional(z.enum(COPILOT_DEFAULT_PERMISSION_MODES))
|
|
7943
|
+
defaultPermissionMode: z.optional(z.enum(COPILOT_DEFAULT_PERMISSION_MODES)),
|
|
7944
|
+
sandbox: z.optional(CopilotSandboxSchema)
|
|
7906
7945
|
});
|
|
7907
7946
|
/**
|
|
7908
7947
|
* oh-my-pi's `tools.approvalMode` values: the tool tiers it auto-approves
|
|
@@ -12835,6 +12874,10 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
12835
12874
|
kind: "replace-owned-keys",
|
|
12836
12875
|
ownedKeys: ["disableAllHooks"]
|
|
12837
12876
|
},
|
|
12877
|
+
mcp: {
|
|
12878
|
+
kind: "replace-owned-keys",
|
|
12879
|
+
ownedKeys: ["disabledMcpServers"]
|
|
12880
|
+
},
|
|
12838
12881
|
permissions: {
|
|
12839
12882
|
kind: "replace-owned-keys",
|
|
12840
12883
|
ownedKeys: ["deniedUrls"]
|
|
@@ -12849,6 +12892,10 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
12849
12892
|
kind: "replace-owned-keys",
|
|
12850
12893
|
ownedKeys: ["disableAllHooks"]
|
|
12851
12894
|
},
|
|
12895
|
+
mcp: {
|
|
12896
|
+
kind: "replace-owned-keys",
|
|
12897
|
+
ownedKeys: ["disabledMcpServers"]
|
|
12898
|
+
},
|
|
12852
12899
|
permissions: {
|
|
12853
12900
|
kind: "replace-owned-keys",
|
|
12854
12901
|
ownedKeys: ["allowedUrls", "deniedUrls"]
|
|
@@ -12866,6 +12913,16 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
12866
12913
|
"chat.tools.edits.autoApprove",
|
|
12867
12914
|
"chat.tools.urls.autoApprove",
|
|
12868
12915
|
"chat.permissions.default",
|
|
12916
|
+
"chat.agent.sandbox.enabled",
|
|
12917
|
+
"chat.agent.sandbox.network.allowNetwork",
|
|
12918
|
+
"chat.agent.sandbox.network.allowLocalNetwork",
|
|
12919
|
+
"chat.agent.sandbox.allowUnsandboxedCommands",
|
|
12920
|
+
"chat.agent.sandbox.mcpServers",
|
|
12921
|
+
"chat.agent.sandbox.lspServers",
|
|
12922
|
+
"chat.agent.sandbox.credentials.authenticategit",
|
|
12923
|
+
"chat.agent.sandbox.credentials.authenticategh",
|
|
12924
|
+
"chat.agent.sandbox.fileSystem.allowDevToolAccess",
|
|
12925
|
+
"chat.agent.sandbox.fileSystem.userConfiguredPaths",
|
|
12869
12926
|
"zoo-code.allowedCommands",
|
|
12870
12927
|
"zoo-code.deniedCommands",
|
|
12871
12928
|
"roo-cline.allowedCommands",
|
|
@@ -22113,7 +22170,7 @@ function getOpencodeCommandEntries({ config, layout = OPENCODE_LAYOUT }) {
|
|
|
22113
22170
|
*/
|
|
22114
22171
|
function getOpencodeSkillPaths({ config, layout = OPENCODE_LAYOUT }) {
|
|
22115
22172
|
if (Array.isArray(config.skills)) {
|
|
22116
|
-
if (!layout.readsV2ConfigSpellings || !isStringArray$
|
|
22173
|
+
if (!layout.readsV2ConfigSpellings || !isStringArray$1(config.skills)) return [];
|
|
22117
22174
|
return config.skills.filter((value) => !/^https?:\/\//i.test(value));
|
|
22118
22175
|
}
|
|
22119
22176
|
const skills = asOpencodeEntries(config.skills);
|
|
@@ -26359,7 +26416,7 @@ const isBooleanValue = ({ value }) => typeof value === "boolean";
|
|
|
26359
26416
|
const isFiniteNumber = ({ value }) => Number.isFinite(value);
|
|
26360
26417
|
const isNonEmptyString = (value) => typeof value === "string" && value !== "";
|
|
26361
26418
|
const isEmittableString = ({ value }) => isNonEmptyString(value);
|
|
26362
|
-
const isEmittableArray = ({ value }) => isStringArray
|
|
26419
|
+
const isEmittableArray = ({ value }) => isStringArray(value);
|
|
26363
26420
|
const isEmittableRecord = ({ value }) => isSafeStringRecord(value);
|
|
26364
26421
|
const isImportableArray = ({ value }) => isSafeStringArray(value);
|
|
26365
26422
|
const isEmittableObject = ({ value }) => isPlainObject$1(value);
|
|
@@ -26801,7 +26858,7 @@ function isStringRecord(value) {
|
|
|
26801
26858
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
26802
26859
|
return Object.values(value).every((v) => typeof v === "string");
|
|
26803
26860
|
}
|
|
26804
|
-
function isStringArray
|
|
26861
|
+
function isStringArray(value) {
|
|
26805
26862
|
return Array.isArray(value) && value.every((v) => typeof v === "string");
|
|
26806
26863
|
}
|
|
26807
26864
|
/** Compare object values without letting key order decide the answer. */
|
|
@@ -26846,7 +26903,7 @@ function isSafeEnvEntry(entry) {
|
|
|
26846
26903
|
* skipped wholesale when that read fails.
|
|
26847
26904
|
*/
|
|
26848
26905
|
function isSafeStringArray(value) {
|
|
26849
|
-
return isStringArray
|
|
26906
|
+
return isStringArray(value) && value.every((entry) => !CONTROL_CHARS.some((char) => entry.includes(char)));
|
|
26850
26907
|
}
|
|
26851
26908
|
/**
|
|
26852
26909
|
* A raw string kept only if the canonical field it would land in accepts it,
|
|
@@ -26888,7 +26945,7 @@ function importTypePayloadFields({ h, hookType, warn }) {
|
|
|
26888
26945
|
return {
|
|
26889
26946
|
...url !== void 0 && { url },
|
|
26890
26947
|
...isStringRecord(headers) && { headers },
|
|
26891
|
-
...isStringArray
|
|
26948
|
+
...isStringArray(h.allowedEnvVars) && { allowedEnvVars: h.allowedEnvVars }
|
|
26892
26949
|
};
|
|
26893
26950
|
}
|
|
26894
26951
|
if (hookType === "mcp_tool") {
|
|
@@ -28028,8 +28085,34 @@ function collectOwnedRefs({ generatedHooks, shape }) {
|
|
|
28028
28085
|
}
|
|
28029
28086
|
return owned;
|
|
28030
28087
|
}
|
|
28088
|
+
/**
|
|
28089
|
+
* The merge keeps every unowned existing handler where it is. Some tools key
|
|
28090
|
+
* state by position — Codex CLI persists a hook's trust review under its event,
|
|
28091
|
+
* matcher group index and handler index — so moving a handler the user already
|
|
28092
|
+
* reviewed would silently change which hook that review belongs to. Hence:
|
|
28093
|
+
* - an unowned existing handler stays at its index;
|
|
28094
|
+
* - a newly generated handler is appended to the first existing group with its
|
|
28095
|
+
* matcher and group-level keys, or in a new group after the existing ones;
|
|
28096
|
+
* - within a group, the slots rulesync's own handlers occupy (including the
|
|
28097
|
+
* appended ones) are filled in generated order, so reordering the sources
|
|
28098
|
+
* still reorders the generated handlers, which matters wherever handlers
|
|
28099
|
+
* run in order (a Qwen Code `sequential` group, a Cursor list).
|
|
28100
|
+
*
|
|
28101
|
+
* A retracted handler, or a group left empty by retractions, is still removed,
|
|
28102
|
+
* which shifts whatever follows it in the same list; that is unavoidable
|
|
28103
|
+
* without leaving a placeholder behind.
|
|
28104
|
+
*/
|
|
28031
28105
|
function mergeMatcherGroups({ existing, generated, event, seen, previouslyOwned, logger }) {
|
|
28032
|
-
const
|
|
28106
|
+
const pending = createPendingGenerated();
|
|
28107
|
+
for (const group of generated) if (isMatcherGroup(group)) addPending({
|
|
28108
|
+
pending,
|
|
28109
|
+
handlers: group.hooks,
|
|
28110
|
+
event,
|
|
28111
|
+
matcher: matcherKey(group.matcher),
|
|
28112
|
+
attributes: groupAttributesKey(group)
|
|
28113
|
+
});
|
|
28114
|
+
const merged = [];
|
|
28115
|
+
const homes = /* @__PURE__ */ new Map();
|
|
28033
28116
|
for (const group of existing) {
|
|
28034
28117
|
if (!isMatcherGroup(group)) {
|
|
28035
28118
|
warnSkip({
|
|
@@ -28040,26 +28123,57 @@ function mergeMatcherGroups({ existing, generated, event, seen, previouslyOwned,
|
|
|
28040
28123
|
continue;
|
|
28041
28124
|
}
|
|
28042
28125
|
const matcher = matcherKey(group.matcher);
|
|
28043
|
-
const
|
|
28044
|
-
|
|
28126
|
+
const hooks = keepExistingHandlers({
|
|
28127
|
+
handlers: group.hooks,
|
|
28045
28128
|
event,
|
|
28046
28129
|
matcher,
|
|
28130
|
+
attributes: groupAttributesKey(group),
|
|
28131
|
+
pending,
|
|
28047
28132
|
seen,
|
|
28048
28133
|
previouslyOwned,
|
|
28049
28134
|
logger
|
|
28050
|
-
})
|
|
28051
|
-
if (
|
|
28052
|
-
const
|
|
28053
|
-
if (target !== void 0 && isMatcherGroup(target)) target.hooks.push(...preserved);
|
|
28054
|
-
else merged.push({
|
|
28135
|
+
});
|
|
28136
|
+
if (hooks.length === 0) continue;
|
|
28137
|
+
const kept = {
|
|
28055
28138
|
...omitPrototypePollutionKeys(group),
|
|
28056
|
-
hooks
|
|
28139
|
+
hooks
|
|
28140
|
+
};
|
|
28141
|
+
merged.push(kept);
|
|
28142
|
+
const home = groupSlotKey(group);
|
|
28143
|
+
if (!homes.has(home)) homes.set(home, kept);
|
|
28144
|
+
}
|
|
28145
|
+
for (const group of generated) {
|
|
28146
|
+
if (!isMatcherGroup(group)) {
|
|
28147
|
+
merged.push(group);
|
|
28148
|
+
continue;
|
|
28149
|
+
}
|
|
28150
|
+
const remaining = group.hooks.filter((handler) => !pending.placed.has(handler));
|
|
28151
|
+
if (remaining.length === 0) continue;
|
|
28152
|
+
const home = homes.get(groupSlotKey(group));
|
|
28153
|
+
if (home === void 0) merged.push({
|
|
28154
|
+
...group,
|
|
28155
|
+
hooks: remaining
|
|
28057
28156
|
});
|
|
28157
|
+
else home.hooks.push(...remaining);
|
|
28058
28158
|
}
|
|
28159
|
+
const order = generatedOrder(generated.flatMap((group) => isMatcherGroup(group) ? group.hooks : []));
|
|
28160
|
+
for (const group of merged) if (isMatcherGroup(group)) orderGeneratedSlots({
|
|
28161
|
+
handlers: group.hooks,
|
|
28162
|
+
order
|
|
28163
|
+
});
|
|
28059
28164
|
return merged;
|
|
28060
28165
|
}
|
|
28166
|
+
/** The flat-list counterpart of {@link mergeMatcherGroups}, with the same positional rules. */
|
|
28061
28167
|
function mergeFlatHandlers({ existing, generated, event, seen, previouslyOwned, logger }) {
|
|
28062
|
-
const
|
|
28168
|
+
const pending = createPendingGenerated();
|
|
28169
|
+
addPending({
|
|
28170
|
+
pending,
|
|
28171
|
+
handlers: generated,
|
|
28172
|
+
event,
|
|
28173
|
+
matcher: void 0,
|
|
28174
|
+
attributes: ""
|
|
28175
|
+
});
|
|
28176
|
+
const flat = [];
|
|
28063
28177
|
for (const handler of existing) {
|
|
28064
28178
|
if (isMatcherGroup(handler)) {
|
|
28065
28179
|
warnSkip({
|
|
@@ -28069,17 +28183,93 @@ function mergeFlatHandlers({ existing, generated, event, seen, previouslyOwned,
|
|
|
28069
28183
|
});
|
|
28070
28184
|
continue;
|
|
28071
28185
|
}
|
|
28072
|
-
|
|
28186
|
+
flat.push(handler);
|
|
28187
|
+
}
|
|
28188
|
+
const merged = keepExistingHandlers({
|
|
28189
|
+
handlers: flat,
|
|
28190
|
+
event,
|
|
28191
|
+
matcher: void 0,
|
|
28192
|
+
attributes: "",
|
|
28193
|
+
pending,
|
|
28194
|
+
seen,
|
|
28195
|
+
previouslyOwned,
|
|
28196
|
+
logger
|
|
28197
|
+
});
|
|
28198
|
+
merged.push(...generated.filter((handler) => !pending.placed.has(handler)));
|
|
28199
|
+
orderGeneratedSlots({
|
|
28200
|
+
handlers: merged,
|
|
28201
|
+
order: generatedOrder(generated)
|
|
28202
|
+
});
|
|
28203
|
+
return merged;
|
|
28204
|
+
}
|
|
28205
|
+
/** Each generated handler's position in the generated list, by object identity. */
|
|
28206
|
+
function generatedOrder(handlers) {
|
|
28207
|
+
const order = /* @__PURE__ */ new Map();
|
|
28208
|
+
for (const [index, handler] of handlers.entries()) if (isPlainObject$1(handler)) order.set(handler, index);
|
|
28209
|
+
return order;
|
|
28210
|
+
}
|
|
28211
|
+
/**
|
|
28212
|
+
* Rewrite, in place, the slots of `handlers` that hold generated handlers so
|
|
28213
|
+
* they follow the generated order. Every other slot — an unowned handler — is
|
|
28214
|
+
* left at its index.
|
|
28215
|
+
*/
|
|
28216
|
+
function orderGeneratedSlots({ handlers, order }) {
|
|
28217
|
+
const slots = [];
|
|
28218
|
+
for (const [index, handler] of handlers.entries()) if (order.has(handler)) slots.push(index);
|
|
28219
|
+
const sorted = slots.map((index) => handlers[index]).toSorted((left, right) => (order.get(left) ?? 0) - (order.get(right) ?? 0));
|
|
28220
|
+
for (const [position, index] of slots.entries()) handlers[index] = sorted[position];
|
|
28221
|
+
}
|
|
28222
|
+
function createPendingGenerated() {
|
|
28223
|
+
return {
|
|
28224
|
+
byKey: /* @__PURE__ */ new Map(),
|
|
28225
|
+
placed: /* @__PURE__ */ new Set()
|
|
28226
|
+
};
|
|
28227
|
+
}
|
|
28228
|
+
function addPending({ pending, handlers, event, matcher, attributes }) {
|
|
28229
|
+
for (const handler of handlers) {
|
|
28230
|
+
if (!isPlainObject$1(handler)) continue;
|
|
28231
|
+
const key = pendingKey({
|
|
28232
|
+
event,
|
|
28233
|
+
matcher,
|
|
28234
|
+
attributes,
|
|
28235
|
+
handler
|
|
28236
|
+
});
|
|
28237
|
+
const queue = pending.byKey.get(key);
|
|
28238
|
+
if (queue === void 0) pending.byKey.set(key, [handler]);
|
|
28239
|
+
else queue.push(handler);
|
|
28240
|
+
}
|
|
28241
|
+
}
|
|
28242
|
+
/**
|
|
28243
|
+
* Walk one existing list in order and decide each handler: replaced in place
|
|
28244
|
+
* by its generated copy, kept as unowned, or dropped (retracted or duplicate).
|
|
28245
|
+
*/
|
|
28246
|
+
function keepExistingHandlers({ handlers, event, matcher, attributes, pending, seen, previouslyOwned, logger }) {
|
|
28247
|
+
const kept = [];
|
|
28248
|
+
for (const handler of handlers) {
|
|
28249
|
+
if (isPlainObject$1(handler)) {
|
|
28250
|
+
const key = pendingKey({
|
|
28251
|
+
event,
|
|
28252
|
+
matcher,
|
|
28253
|
+
attributes,
|
|
28254
|
+
handler
|
|
28255
|
+
});
|
|
28256
|
+
const generated = pending.byKey.get(key)?.shift();
|
|
28257
|
+
if (generated !== void 0) {
|
|
28258
|
+
pending.placed.add(generated);
|
|
28259
|
+
kept.push(generated);
|
|
28260
|
+
continue;
|
|
28261
|
+
}
|
|
28262
|
+
}
|
|
28263
|
+
if (shouldPreserve({
|
|
28073
28264
|
handler,
|
|
28074
28265
|
event,
|
|
28075
|
-
matcher
|
|
28266
|
+
matcher,
|
|
28076
28267
|
seen,
|
|
28077
28268
|
previouslyOwned,
|
|
28078
28269
|
logger
|
|
28079
|
-
}))
|
|
28080
|
-
preserved.push(cloneHandler(handler));
|
|
28270
|
+
})) kept.push(cloneHandler(handler));
|
|
28081
28271
|
}
|
|
28082
|
-
return
|
|
28272
|
+
return kept;
|
|
28083
28273
|
}
|
|
28084
28274
|
function isMatcherGroup(value) {
|
|
28085
28275
|
return isPlainObject$1(value) && Array.isArray(value.hooks);
|
|
@@ -28170,6 +28360,36 @@ function stableStringify$1(value) {
|
|
|
28170
28360
|
function matcherKey(matcher) {
|
|
28171
28361
|
return matcher === void 0 ? "" : stableStringify$1(matcher);
|
|
28172
28362
|
}
|
|
28363
|
+
function pendingKey({ event, matcher, attributes, handler }) {
|
|
28364
|
+
const key = ownedHookKey({
|
|
28365
|
+
event,
|
|
28366
|
+
matcher,
|
|
28367
|
+
identity: handlerIdentity(handler)
|
|
28368
|
+
});
|
|
28369
|
+
return JSON.stringify([key, attributes]);
|
|
28370
|
+
}
|
|
28371
|
+
/** Groups that share a matcher and attributes are interchangeable homes for a handler. */
|
|
28372
|
+
function groupSlotKey(group) {
|
|
28373
|
+
return JSON.stringify([matcherKey(group.matcher), groupAttributesKey(group)]);
|
|
28374
|
+
}
|
|
28375
|
+
/** Group-level keys whose default value is known, so an explicit default can be ignored. */
|
|
28376
|
+
const GROUP_ATTRIBUTE_DEFAULTS = Object.freeze({
|
|
28377
|
+
__proto__: null,
|
|
28378
|
+
sequential: false
|
|
28379
|
+
});
|
|
28380
|
+
/**
|
|
28381
|
+
* The stable form of a matcher group's keys other than `matcher` and `hooks`.
|
|
28382
|
+
*
|
|
28383
|
+
* A key set to its known default is the same group as one that omits it:
|
|
28384
|
+
* Qwen Code's `sequential` defaults to `false` and rulesync emits it only when
|
|
28385
|
+
* `true`, so an explicit `sequential: false` (or any `undefined` key) is
|
|
28386
|
+
* dropped before comparing. Other `false` values are kept, since their default
|
|
28387
|
+
* is not known here.
|
|
28388
|
+
*/
|
|
28389
|
+
function groupAttributesKey(group) {
|
|
28390
|
+
const { matcher: _matcher, hooks: _hooks, ...attributes } = group;
|
|
28391
|
+
return stableStringify$1(Object.fromEntries(Object.entries(attributes).filter(([key, value]) => value !== void 0 && GROUP_ATTRIBUTE_DEFAULTS[key] !== value)));
|
|
28392
|
+
}
|
|
28173
28393
|
function warnSkip({ logger, event, expected }) {
|
|
28174
28394
|
logger?.warn(`Skipping existing hook entry on ${event}: expected ${expected}`);
|
|
28175
28395
|
}
|
|
@@ -28665,7 +28885,7 @@ const MANAGED_EVENT_NAMES = new Set(Object.values(CANONICAL_TO_CLINE_EVENT_NAMES
|
|
|
28665
28885
|
function parseManifest(fileContent) {
|
|
28666
28886
|
try {
|
|
28667
28887
|
const parsed = JSON.parse(fileContent);
|
|
28668
|
-
if (!isRecord$1(parsed) || !isStringArray$
|
|
28888
|
+
if (!isRecord$1(parsed) || !isStringArray$1(parsed.events)) return null;
|
|
28669
28889
|
return {
|
|
28670
28890
|
generatedBy: "rulesync",
|
|
28671
28891
|
events: parsed.events.filter((event) => MANAGED_EVENT_NAMES.has(event))
|
|
@@ -30203,7 +30423,7 @@ function copilotCliHooksToCanonical(rawHooks, logger) {
|
|
|
30203
30423
|
* see the CLI config dir reference:
|
|
30204
30424
|
* https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference
|
|
30205
30425
|
*/
|
|
30206
|
-
function copilotCliSettingsPaths({ global }) {
|
|
30426
|
+
function copilotCliSettingsPaths$1({ global }) {
|
|
30207
30427
|
return global ? {
|
|
30208
30428
|
relativeDirPath: COPILOT_DIR,
|
|
30209
30429
|
relativeFilePath: COPILOTCLI_SETTINGS_FILE_NAME
|
|
@@ -30218,7 +30438,7 @@ function copilotCliSettingsPaths({ global }) {
|
|
|
30218
30438
|
* subject, and the permissions feature reports a broken settings file itself.
|
|
30219
30439
|
*/
|
|
30220
30440
|
async function readDisableAllHooks({ outputRoot, global }) {
|
|
30221
|
-
const paths = copilotCliSettingsPaths({ global });
|
|
30441
|
+
const paths = copilotCliSettingsPaths$1({ global });
|
|
30222
30442
|
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath));
|
|
30223
30443
|
if (fileContent === null) return;
|
|
30224
30444
|
try {
|
|
@@ -30248,7 +30468,7 @@ var CopilotcliHooksSettings = class CopilotcliHooksSettings extends ToolFile {
|
|
|
30248
30468
|
return false;
|
|
30249
30469
|
}
|
|
30250
30470
|
static async fromDisableAllHooks({ outputRoot = process.cwd(), global = false, disableAllHooks }) {
|
|
30251
|
-
const paths = copilotCliSettingsPaths({ global });
|
|
30471
|
+
const paths = copilotCliSettingsPaths$1({ global });
|
|
30252
30472
|
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
30253
30473
|
const existingContent = await readFileContentOrNull(filePath) ?? "{}";
|
|
30254
30474
|
return new CopilotcliHooksSettings({
|
|
@@ -30278,7 +30498,7 @@ var CopilotcliHooks = class CopilotcliHooks extends ToolHooks {
|
|
|
30278
30498
|
this.disableAllHooks = disableAllHooks;
|
|
30279
30499
|
}
|
|
30280
30500
|
static getExtraSharedWritePaths({ global = false } = {}) {
|
|
30281
|
-
return [copilotCliSettingsPaths({ global })];
|
|
30501
|
+
return [copilotCliSettingsPaths$1({ global })];
|
|
30282
30502
|
}
|
|
30283
30503
|
static async getAuxiliaryFiles({ toolHooks } = {}) {
|
|
30284
30504
|
return toolHooks instanceof CopilotcliHooks && toolHooks.settingsFile ? [toolHooks.settingsFile] : [];
|
|
@@ -34722,12 +34942,21 @@ var QwencodeHooks = class QwencodeHooks extends ToolHooks {
|
|
|
34722
34942
|
validate
|
|
34723
34943
|
});
|
|
34724
34944
|
}
|
|
34725
|
-
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, logger }) {
|
|
34945
|
+
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, preserveUnowned = false, logger }) {
|
|
34726
34946
|
const paths = QwencodeHooks.getSettablePaths({ global });
|
|
34727
34947
|
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
34728
34948
|
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
|
|
34729
34949
|
const config = rulesyncHooks.getJson();
|
|
34730
|
-
const
|
|
34950
|
+
const previouslyOwned = preserveUnowned ? parseHooksOwnershipLock(await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, HOOKS_OWNERSHIP_LOCK_FILE_NAME))) : void 0;
|
|
34951
|
+
const merged = mergeGeneratedHookLists({
|
|
34952
|
+
existingContent,
|
|
34953
|
+
generatedHooks: canonicalToQwencodeHooks(config, logger),
|
|
34954
|
+
shape: "matcher-groups",
|
|
34955
|
+
preserveUnowned,
|
|
34956
|
+
previouslyOwned,
|
|
34957
|
+
logger
|
|
34958
|
+
});
|
|
34959
|
+
const patch = { hooks: merged.hooks };
|
|
34731
34960
|
const disableAllHooks = config.qwencode?.disableAllHooks;
|
|
34732
34961
|
if (typeof disableAllHooks === "boolean") patch.disableAllHooks = disableAllHooks;
|
|
34733
34962
|
const fileContent = applySharedConfigPatch({
|
|
@@ -34742,9 +34971,16 @@ var QwencodeHooks = class QwencodeHooks extends ToolHooks {
|
|
|
34742
34971
|
relativeDirPath: paths.relativeDirPath,
|
|
34743
34972
|
relativeFilePath: paths.relativeFilePath,
|
|
34744
34973
|
fileContent,
|
|
34974
|
+
ownedHookRefs: preserveUnowned ? merged.owned : void 0,
|
|
34745
34975
|
validate
|
|
34746
34976
|
});
|
|
34747
34977
|
}
|
|
34978
|
+
static supportsPreserveUnowned() {
|
|
34979
|
+
return true;
|
|
34980
|
+
}
|
|
34981
|
+
static async getAuxiliaryFiles({ toolHooks } = {}) {
|
|
34982
|
+
return toolHooks instanceof QwencodeHooks ? toolHooks.getOwnershipLockFiles() : [];
|
|
34983
|
+
}
|
|
34748
34984
|
toRulesyncHooks() {
|
|
34749
34985
|
let settings;
|
|
34750
34986
|
try {
|
|
@@ -41144,7 +41380,7 @@ function isCodexStdioServer(config) {
|
|
|
41144
41380
|
* @see https://learn.chatgpt.com/docs/extend/mcp
|
|
41145
41381
|
*/
|
|
41146
41382
|
function isValidRenamedArray(key, value) {
|
|
41147
|
-
return key === "env_vars" || key === "envVars" ? isEnvVarEntryArray(value) : isStringArray$
|
|
41383
|
+
return key === "env_vars" || key === "envVars" ? isEnvVarEntryArray(value) : isStringArray$1(value);
|
|
41148
41384
|
}
|
|
41149
41385
|
/**
|
|
41150
41386
|
* The keys of Codex's `McpServerOAuthConfig`. Anything else in a server's
|
|
@@ -42363,18 +42599,6 @@ function renameEnabledToolsToTools({ server, serverName, logger }) {
|
|
|
42363
42599
|
};
|
|
42364
42600
|
}
|
|
42365
42601
|
/**
|
|
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
42602
|
* Resolves and sets the transport type for each MCP server config.
|
|
42379
42603
|
* GitHub Copilot CLI requires the "type" field for each server. A server it
|
|
42380
42604
|
* cannot express — one that names no transport, or names one it carries no way
|
|
@@ -42416,7 +42640,7 @@ function addTypeField(mcpServers, logger) {
|
|
|
42416
42640
|
});
|
|
42417
42641
|
continue;
|
|
42418
42642
|
}
|
|
42419
|
-
const { httpUrl: _httpUrl, ...rest } = parsed;
|
|
42643
|
+
const { httpUrl: _httpUrl, disabled: _disabled, ...rest } = parsed;
|
|
42420
42644
|
result[name] = {
|
|
42421
42645
|
...renameEnabledToolsToTools({
|
|
42422
42646
|
server: rest,
|
|
@@ -42428,7 +42652,7 @@ function addTypeField(mcpServers, logger) {
|
|
|
42428
42652
|
};
|
|
42429
42653
|
continue;
|
|
42430
42654
|
}
|
|
42431
|
-
const { httpUrl: _localHttpUrl, ...local } = parsed;
|
|
42655
|
+
const { httpUrl: _localHttpUrl, disabled: _localDisabled, ...local } = parsed;
|
|
42432
42656
|
const [head, ...tail] = resolveLocalMcpCommand(parsed);
|
|
42433
42657
|
if (head === void 0) {
|
|
42434
42658
|
warnAndSkipMcpServer({
|
|
@@ -42459,7 +42683,7 @@ function removeTypeField(config) {
|
|
|
42459
42683
|
const result = {};
|
|
42460
42684
|
for (const [name, server] of Object.entries(config.mcpServers ?? {})) {
|
|
42461
42685
|
const { tools, ...withoutTools } = server;
|
|
42462
|
-
const restored = isStringArray(tools) ? {
|
|
42686
|
+
const restored = isStringArray$1(tools) ? {
|
|
42463
42687
|
...withoutTools,
|
|
42464
42688
|
enabledTools: tools
|
|
42465
42689
|
} : {
|
|
@@ -42475,6 +42699,99 @@ function removeTypeField(config) {
|
|
|
42475
42699
|
}
|
|
42476
42700
|
return result;
|
|
42477
42701
|
}
|
|
42702
|
+
/**
|
|
42703
|
+
* The Copilot CLI settings file carrying `disabledMcpServers`:
|
|
42704
|
+
* `.github/copilot/settings.json` (project) / `~/.copilot/settings.json`
|
|
42705
|
+
* (global). Both scopes document the key ("MCP server names to disable. Listed
|
|
42706
|
+
* servers are configured but not started"); at repository scope it is
|
|
42707
|
+
* union-merged with the user value, so a repository can add names but never
|
|
42708
|
+
* remove them.
|
|
42709
|
+
*
|
|
42710
|
+
* @see https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference
|
|
42711
|
+
*/
|
|
42712
|
+
function copilotCliSettingsPaths({ global }) {
|
|
42713
|
+
return global ? {
|
|
42714
|
+
relativeDirPath: COPILOT_DIR,
|
|
42715
|
+
relativeFilePath: COPILOTCLI_SETTINGS_FILE_NAME
|
|
42716
|
+
} : {
|
|
42717
|
+
relativeDirPath: COPILOTCLI_PROJECT_SETTINGS_DIR_PATH,
|
|
42718
|
+
relativeFilePath: COPILOTCLI_SETTINGS_FILE_NAME
|
|
42719
|
+
};
|
|
42720
|
+
}
|
|
42721
|
+
async function readCopilotCliSettings({ outputRoot, global }) {
|
|
42722
|
+
const paths = copilotCliSettingsPaths({ global });
|
|
42723
|
+
const existingContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "";
|
|
42724
|
+
try {
|
|
42725
|
+
return {
|
|
42726
|
+
paths,
|
|
42727
|
+
existingContent,
|
|
42728
|
+
document: parseSharedConfig({
|
|
42729
|
+
format: "json",
|
|
42730
|
+
fileContent: existingContent,
|
|
42731
|
+
filePath: join(paths.relativeDirPath, paths.relativeFilePath),
|
|
42732
|
+
invalidRootPolicy: "error"
|
|
42733
|
+
})
|
|
42734
|
+
};
|
|
42735
|
+
} catch (error) {
|
|
42736
|
+
return {
|
|
42737
|
+
paths,
|
|
42738
|
+
existingContent,
|
|
42739
|
+
document: null,
|
|
42740
|
+
error
|
|
42741
|
+
};
|
|
42742
|
+
}
|
|
42743
|
+
}
|
|
42744
|
+
/**
|
|
42745
|
+
* The names of the servers rulesync actually writes for this target: one
|
|
42746
|
+
* `addTypeField` skips (no transport, WebSocket, missing url or command) is
|
|
42747
|
+
* not managed, so its name neither gains a toggle nor strips a same-named
|
|
42748
|
+
* user-authored entry from the settings file.
|
|
42749
|
+
*/
|
|
42750
|
+
function managedServerNames(mcpServers) {
|
|
42751
|
+
return Object.keys(addTypeField(mcpServers) ?? {});
|
|
42752
|
+
}
|
|
42753
|
+
/**
|
|
42754
|
+
* Overlays `disabled: true` on the servers the scope's settings file lists in
|
|
42755
|
+
* `disabledMcpServers`, so import round-trips the toggle into the canonical
|
|
42756
|
+
* config. A settings file that exists but cannot be parsed throws
|
|
42757
|
+
* (fail-closed): importing past it would re-enable every disabled server.
|
|
42758
|
+
*/
|
|
42759
|
+
async function overlayDisabledServers({ mcpServers, outputRoot, global }) {
|
|
42760
|
+
const settings = await readCopilotCliSettings({
|
|
42761
|
+
outputRoot,
|
|
42762
|
+
global
|
|
42763
|
+
});
|
|
42764
|
+
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 });
|
|
42765
|
+
const disabled = settings.document.disabledMcpServers;
|
|
42766
|
+
if (!isStringArray$1(disabled) || disabled.length === 0) return mcpServers;
|
|
42767
|
+
const servers = { ...mcpServers };
|
|
42768
|
+
for (const name of disabled) {
|
|
42769
|
+
if (!Object.hasOwn(servers, name)) continue;
|
|
42770
|
+
const server = servers[name];
|
|
42771
|
+
if (isPlainObject$1(server)) servers[name] = {
|
|
42772
|
+
...server,
|
|
42773
|
+
disabled: true
|
|
42774
|
+
};
|
|
42775
|
+
}
|
|
42776
|
+
return servers;
|
|
42777
|
+
}
|
|
42778
|
+
/**
|
|
42779
|
+
* Auxiliary writer for `disabledMcpServers` in the Copilot CLI settings file.
|
|
42780
|
+
* Only that key is owned (through the shared-config gateway), so `deniedUrls`,
|
|
42781
|
+
* `disableAllHooks`, `model` and every other setting are preserved. It is a
|
|
42782
|
+
* shared, user-owned file and is never deleted.
|
|
42783
|
+
*/
|
|
42784
|
+
var CopilotcliMcpSettings = class extends ToolFile {
|
|
42785
|
+
validate() {
|
|
42786
|
+
return {
|
|
42787
|
+
success: true,
|
|
42788
|
+
error: null
|
|
42789
|
+
};
|
|
42790
|
+
}
|
|
42791
|
+
isDeletable() {
|
|
42792
|
+
return false;
|
|
42793
|
+
}
|
|
42794
|
+
};
|
|
42478
42795
|
var CopilotcliMcp = class CopilotcliMcp extends ToolMcp {
|
|
42479
42796
|
json;
|
|
42480
42797
|
constructor(params) {
|
|
@@ -42517,7 +42834,11 @@ var CopilotcliMcp = class CopilotcliMcp extends ToolMcp {
|
|
|
42517
42834
|
const json = JSON.parse(fileContent);
|
|
42518
42835
|
const newJson = {
|
|
42519
42836
|
...json,
|
|
42520
|
-
mcpServers:
|
|
42837
|
+
mcpServers: await overlayDisabledServers({
|
|
42838
|
+
mcpServers: json.mcpServers ?? {},
|
|
42839
|
+
outputRoot,
|
|
42840
|
+
global
|
|
42841
|
+
})
|
|
42521
42842
|
};
|
|
42522
42843
|
return new CopilotcliMcp({
|
|
42523
42844
|
outputRoot,
|
|
@@ -42532,7 +42853,17 @@ var CopilotcliMcp = class CopilotcliMcp extends ToolMcp {
|
|
|
42532
42853
|
const paths = this.getSettablePaths({ global });
|
|
42533
42854
|
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? JSON.stringify({ mcpServers: {} }, null, 2);
|
|
42534
42855
|
const json = JSON.parse(fileContent);
|
|
42535
|
-
const
|
|
42856
|
+
const settings = await readCopilotCliSettings({
|
|
42857
|
+
outputRoot,
|
|
42858
|
+
global
|
|
42859
|
+
});
|
|
42860
|
+
const existingDisabled = settings.document?.disabledMcpServers;
|
|
42861
|
+
const offSwitchBlocker = settings.document === null ? "cannot be parsed" : existingDisabled !== void 0 && !isStringArray$1(existingDisabled) ? "has a disabledMcpServers value that is not an array of server names" : void 0;
|
|
42862
|
+
const copilotCliMcpServers = addTypeField(Object.fromEntries(Object.entries(rulesyncMcp.getMcpServers()).filter(([name, server]) => {
|
|
42863
|
+
if (server.disabled !== true || offSwitchBlocker === void 0) return true;
|
|
42864
|
+
logger?.warn(`[CopilotcliMcp] Skipping disabled MCP server "${name}": ${join(settings.paths.relativeDirPath, settings.paths.relativeFilePath)} ${offSwitchBlocker}, so disabledMcpServers cannot be written to switch it off.`);
|
|
42865
|
+
return false;
|
|
42866
|
+
})), logger);
|
|
42536
42867
|
const mcpJson = {
|
|
42537
42868
|
...json,
|
|
42538
42869
|
mcpServers: copilotCliMcpServers
|
|
@@ -42546,6 +42877,66 @@ var CopilotcliMcp = class CopilotcliMcp extends ToolMcp {
|
|
|
42546
42877
|
global
|
|
42547
42878
|
});
|
|
42548
42879
|
}
|
|
42880
|
+
/**
|
|
42881
|
+
* `disabledMcpServers` lives in the Copilot CLI settings file the hooks and
|
|
42882
|
+
* permissions features also write. Declared here so the write-order
|
|
42883
|
+
* derivation sees this feature as one of that file's writers — it is not a
|
|
42884
|
+
* settable path, since the servers themselves live in the MCP file.
|
|
42885
|
+
*/
|
|
42886
|
+
static getExtraSharedWritePaths({ global = false } = {}) {
|
|
42887
|
+
return [copilotCliSettingsPaths({ global })];
|
|
42888
|
+
}
|
|
42889
|
+
/**
|
|
42890
|
+
* Routes the canonical `disabled: true` to `disabledMcpServers`, the key
|
|
42891
|
+
* Copilot CLI actually consults: an MCP file entry has no `disabled` field.
|
|
42892
|
+
* rulesync owns the toggle for the servers it writes; names it does not
|
|
42893
|
+
* manage keep their existing state. The file is not created or touched when
|
|
42894
|
+
* there is nothing to disable and nothing to retract, nor when the existing
|
|
42895
|
+
* `disabledMcpServers` is not a string array (left as is, with a warning).
|
|
42896
|
+
*/
|
|
42897
|
+
static async getAuxiliaryFiles({ outputRoot = process.cwd(), global = false, rulesyncMcp, logger }) {
|
|
42898
|
+
const servers = rulesyncMcp.forTarget({
|
|
42899
|
+
toolTarget: "copilotcli",
|
|
42900
|
+
global
|
|
42901
|
+
}).getMcpServers();
|
|
42902
|
+
const managedNames = managedServerNames(servers);
|
|
42903
|
+
const disabledNames = managedNames.filter((name) => servers[name]?.disabled === true);
|
|
42904
|
+
const settings = await readCopilotCliSettings({
|
|
42905
|
+
outputRoot,
|
|
42906
|
+
global
|
|
42907
|
+
});
|
|
42908
|
+
if (settings.document === null) {
|
|
42909
|
+
logger?.warn(`Skipping the Copilot CLI disabledMcpServers update: ${formatError(settings.error)}`);
|
|
42910
|
+
return [];
|
|
42911
|
+
}
|
|
42912
|
+
const existing = settings.document.disabledMcpServers;
|
|
42913
|
+
if (existing !== void 0 && !isStringArray$1(existing)) {
|
|
42914
|
+
logger?.warn(`Skipping the Copilot CLI disabledMcpServers update: the existing value is not an array of server names. Fix it in ${join(settings.paths.relativeDirPath, settings.paths.relativeFilePath)} so rulesync can manage the toggle.`);
|
|
42915
|
+
return [];
|
|
42916
|
+
}
|
|
42917
|
+
const existingDisabled = existing ?? [];
|
|
42918
|
+
const managedNameSet = new Set(managedNames);
|
|
42919
|
+
const reEnabled = existingDisabled.filter((name) => managedNameSet.has(name) && !disabledNames.includes(name));
|
|
42920
|
+
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.`);
|
|
42921
|
+
const mergedDisabled = [...existingDisabled.filter((name) => !managedNameSet.has(name)), ...disabledNames].toSorted();
|
|
42922
|
+
if (mergedDisabled.length === 0 && existing === void 0) return [];
|
|
42923
|
+
if (isStringArray$1(existing) && existing.length === mergedDisabled.length && existing.toSorted().every((name, index) => name === mergedDisabled[index])) return [];
|
|
42924
|
+
const { paths } = settings;
|
|
42925
|
+
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
42926
|
+
return [new CopilotcliMcpSettings({
|
|
42927
|
+
outputRoot,
|
|
42928
|
+
relativeDirPath: paths.relativeDirPath,
|
|
42929
|
+
relativeFilePath: paths.relativeFilePath,
|
|
42930
|
+
fileContent: applySharedConfigPatch({
|
|
42931
|
+
fileKey: sharedConfigFileKey(paths),
|
|
42932
|
+
feature: "mcp",
|
|
42933
|
+
existingContent: settings.existingContent,
|
|
42934
|
+
patch: { disabledMcpServers: mergedDisabled.length > 0 ? mergedDisabled : void 0 },
|
|
42935
|
+
filePath
|
|
42936
|
+
}),
|
|
42937
|
+
global
|
|
42938
|
+
})];
|
|
42939
|
+
}
|
|
42549
42940
|
toRulesyncMcp() {
|
|
42550
42941
|
const mcpServers = removeTypeField(this.json);
|
|
42551
42942
|
return this.toRulesyncMcpDefault({ fileContent: JSON.stringify({ mcpServers }, null, 2) });
|
|
@@ -42977,10 +43368,10 @@ function convertFromCrushFormat(crushServers) {
|
|
|
42977
43368
|
if (value === "http" || value === "sse") converted.type = value;
|
|
42978
43369
|
break;
|
|
42979
43370
|
case "disabled_tools":
|
|
42980
|
-
if (isStringArray$
|
|
43371
|
+
if (isStringArray$1(value)) converted.disabledTools = value;
|
|
42981
43372
|
break;
|
|
42982
43373
|
case "enabled_tools":
|
|
42983
|
-
if (isStringArray$
|
|
43374
|
+
if (isStringArray$1(value)) converted.enabledTools = value;
|
|
42984
43375
|
break;
|
|
42985
43376
|
case "oauth_token": break;
|
|
42986
43377
|
default: {
|
|
@@ -43967,7 +44358,7 @@ function convertRowsFromDsh(rows, logger) {
|
|
|
43967
44358
|
} else {
|
|
43968
44359
|
server.type = "stdio";
|
|
43969
44360
|
if (typeof config.command === "string") server.command = config.command;
|
|
43970
|
-
if (isStringArray$
|
|
44361
|
+
if (isStringArray$1(config.args)) server.args = config.args;
|
|
43971
44362
|
const env = copyStringRecord(config.env);
|
|
43972
44363
|
if (env !== void 0) server.env = env;
|
|
43973
44364
|
if (typeof config.cwd === "string") server.cwd = config.cwd;
|
|
@@ -44398,11 +44789,11 @@ function applyGooseStdioFields(ext, config) {
|
|
|
44398
44789
|
if (Array.isArray(command)) {
|
|
44399
44790
|
if (typeof command[0] === "string") ext.cmd = command[0];
|
|
44400
44791
|
const rest = command.slice(1).filter((c) => typeof c === "string");
|
|
44401
|
-
const args = isStringArray$
|
|
44792
|
+
const args = isStringArray$1(config.args) ? config.args : [];
|
|
44402
44793
|
if (rest.length > 0 || args.length > 0) ext.args = [...rest, ...args];
|
|
44403
44794
|
} else if (typeof command === "string") {
|
|
44404
44795
|
ext.cmd = command;
|
|
44405
|
-
if (isStringArray$
|
|
44796
|
+
if (isStringArray$1(config.args)) ext.args = config.args;
|
|
44406
44797
|
}
|
|
44407
44798
|
if (isPlainObject$1(config.env)) ext.envs = omitPrototypePollutionKeys(config.env);
|
|
44408
44799
|
if (typeof config.cwd === "string") ext.cwd = config.cwd;
|
|
@@ -44505,7 +44896,7 @@ function convertFromGooseFormat(extensions) {
|
|
|
44505
44896
|
else if (type === "streamable_http") server.type = "http";
|
|
44506
44897
|
else server.type = "stdio";
|
|
44507
44898
|
if (typeof ext.cmd === "string") server.command = ext.cmd;
|
|
44508
|
-
if (isStringArray$
|
|
44899
|
+
if (isStringArray$1(ext.args)) server.args = ext.args;
|
|
44509
44900
|
if (isPlainObject$1(ext.envs)) server.env = omitPrototypePollutionKeys(ext.envs);
|
|
44510
44901
|
if (typeof ext.cwd === "string") server.cwd = ext.cwd;
|
|
44511
44902
|
if (typeof ext.uri === "string") server.url = ext.uri;
|
|
@@ -44527,11 +44918,11 @@ function buildGoosePluginStdioServer(config) {
|
|
|
44527
44918
|
if (Array.isArray(command)) {
|
|
44528
44919
|
if (typeof command[0] === "string") server.command = command[0];
|
|
44529
44920
|
const rest = command.slice(1).filter((c) => typeof c === "string");
|
|
44530
|
-
const args = isStringArray$
|
|
44921
|
+
const args = isStringArray$1(config.args) ? config.args : [];
|
|
44531
44922
|
if (rest.length > 0 || args.length > 0) server.args = [...rest, ...args];
|
|
44532
44923
|
} else if (typeof command === "string") {
|
|
44533
44924
|
server.command = command;
|
|
44534
|
-
if (isStringArray$
|
|
44925
|
+
if (isStringArray$1(config.args)) server.args = config.args;
|
|
44535
44926
|
}
|
|
44536
44927
|
if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
44537
44928
|
if (typeof config.cwd === "string") server.cwd = config.cwd;
|
|
@@ -44939,7 +45330,7 @@ function copyHermesOauth(source) {
|
|
|
44939
45330
|
if (!isRecord$1(source)) return;
|
|
44940
45331
|
const oauth = {};
|
|
44941
45332
|
for (const key of HERMES_OAUTH_STRING_KEYS) if (typeof source[key] === "string") oauth[key] = source[key];
|
|
44942
|
-
if (typeof oauth.scope !== "string" && isStringArray$
|
|
45333
|
+
if (typeof oauth.scope !== "string" && isStringArray$1(source.scopes)) oauth.scope = source.scopes.join(" ");
|
|
44943
45334
|
for (const key of ["redirect_port", "timeout"]) if (typeof source[key] === "number") oauth[key] = source[key];
|
|
44944
45335
|
if (typeof source.cimd === "boolean") oauth.cimd = source.cimd;
|
|
44945
45336
|
return Object.keys(oauth).length > 0 ? oauth : void 0;
|
|
@@ -44966,7 +45357,7 @@ function copyHermesAdvancedFields(source, target) {
|
|
|
44966
45357
|
target.auth = source.auth;
|
|
44967
45358
|
copied = true;
|
|
44968
45359
|
}
|
|
44969
|
-
if (typeof source.client_cert === "string" || isStringArray$
|
|
45360
|
+
if (typeof source.client_cert === "string" || isStringArray$1(source.client_cert)) {
|
|
44970
45361
|
target.client_cert = source.client_cert;
|
|
44971
45362
|
copied = true;
|
|
44972
45363
|
}
|
|
@@ -45038,8 +45429,8 @@ function copyHermesAdvancedFields(source, target) {
|
|
|
45038
45429
|
*/
|
|
45039
45430
|
function buildHermesToolsBlock(config) {
|
|
45040
45431
|
const tools = {};
|
|
45041
|
-
if (isStringArray$
|
|
45042
|
-
if (isStringArray$
|
|
45432
|
+
if (isStringArray$1(config.enabledTools)) tools.include = config.enabledTools;
|
|
45433
|
+
if (isStringArray$1(config.disabledTools)) tools.exclude = config.disabledTools;
|
|
45043
45434
|
if (typeof config.promptsEnabled === "boolean") tools.prompts = config.promptsEnabled;
|
|
45044
45435
|
if (typeof config.resourcesEnabled === "boolean") tools.resources = config.resourcesEnabled;
|
|
45045
45436
|
return tools;
|
|
@@ -45051,8 +45442,8 @@ function buildHermesToolsBlock(config) {
|
|
|
45051
45442
|
* `promptsEnabled`/`resourcesEnabled` top-level toggles.
|
|
45052
45443
|
*/
|
|
45053
45444
|
function applyHermesToolsBlock(hermesTools, server) {
|
|
45054
|
-
if (isStringArray$
|
|
45055
|
-
if (isStringArray$
|
|
45445
|
+
if (isStringArray$1(hermesTools.include)) server.enabledTools = hermesTools.include;
|
|
45446
|
+
if (isStringArray$1(hermesTools.exclude)) server.disabledTools = hermesTools.exclude;
|
|
45056
45447
|
if (typeof hermesTools.prompts === "boolean") server.promptsEnabled = hermesTools.prompts;
|
|
45057
45448
|
if (typeof hermesTools.resources === "boolean") server.resourcesEnabled = hermesTools.resources;
|
|
45058
45449
|
}
|
|
@@ -45078,11 +45469,11 @@ function convertServerToHermes(config) {
|
|
|
45078
45469
|
if (Array.isArray(command)) {
|
|
45079
45470
|
if (typeof command[0] === "string") out.command = command[0];
|
|
45080
45471
|
const rest = command.slice(1).filter((c) => typeof c === "string");
|
|
45081
|
-
const args = isStringArray$
|
|
45472
|
+
const args = isStringArray$1(config.args) ? config.args : [];
|
|
45082
45473
|
if (rest.length > 0 || args.length > 0) out.args = [...rest, ...args];
|
|
45083
45474
|
} else if (typeof command === "string") {
|
|
45084
45475
|
out.command = command;
|
|
45085
|
-
if (isStringArray$
|
|
45476
|
+
if (isStringArray$1(config.args)) out.args = config.args;
|
|
45086
45477
|
}
|
|
45087
45478
|
if (isPlainObject$1(config.env)) out.env = omitPrototypePollutionKeys(config.env);
|
|
45088
45479
|
} else if (url !== void 0) {
|
|
@@ -45132,7 +45523,7 @@ function convertFromHermesFormat(mcpServers) {
|
|
|
45132
45523
|
if (PROTOTYPE_POLLUTION_KEYS.has(name) || !isRecord$1(config)) continue;
|
|
45133
45524
|
const server = {};
|
|
45134
45525
|
if (typeof config.command === "string") server.command = config.command;
|
|
45135
|
-
if (isStringArray$
|
|
45526
|
+
if (isStringArray$1(config.args)) server.args = config.args;
|
|
45136
45527
|
if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
45137
45528
|
if (typeof config.url === "string") server.url = config.url;
|
|
45138
45529
|
if (isPlainObject$1(config.headers)) server.headers = omitPrototypePollutionKeys(config.headers);
|
|
@@ -46324,7 +46715,7 @@ function mergeToolLists(...lists) {
|
|
|
46324
46715
|
function toKiroMcpServers(servers) {
|
|
46325
46716
|
return Object.fromEntries(Object.entries(servers).map(([name, server]) => {
|
|
46326
46717
|
const { kiroAutoApprove, kiroAutoBlock, disabledTools, ...rest } = server;
|
|
46327
|
-
const autoApprove = mergeToolLists(isStringArray$
|
|
46718
|
+
const autoApprove = mergeToolLists(isStringArray$1(rest.autoApprove) ? rest.autoApprove : void 0, kiroAutoApprove);
|
|
46328
46719
|
const disabled = mergeToolLists(disabledTools, kiroAutoBlock);
|
|
46329
46720
|
return [name, {
|
|
46330
46721
|
...rest,
|
|
@@ -46348,7 +46739,7 @@ function fromKiroMcpServers(servers) {
|
|
|
46348
46739
|
return Object.fromEntries(Object.entries(servers).map(([name, server]) => {
|
|
46349
46740
|
if (server === null || typeof server !== "object" || Array.isArray(server)) return [name, server];
|
|
46350
46741
|
const { autoApprove, ...rest } = server;
|
|
46351
|
-
if (!isStringArray$
|
|
46742
|
+
if (!isStringArray$1(autoApprove)) return [name, server];
|
|
46352
46743
|
return [name, {
|
|
46353
46744
|
...rest,
|
|
46354
46745
|
kiroAutoApprove: autoApprove
|
|
@@ -47777,7 +48168,7 @@ function headersRecordToPoolList(headers) {
|
|
|
47777
48168
|
return Object.entries(headers).map(([name, value]) => `${name}: ${value}`);
|
|
47778
48169
|
}
|
|
47779
48170
|
function poolHeadersListToRecord(serverName, headers) {
|
|
47780
|
-
if (!isStringArray$
|
|
48171
|
+
if (!isStringArray$1(headers)) return void 0;
|
|
47781
48172
|
const result = {};
|
|
47782
48173
|
for (const entry of headers) {
|
|
47783
48174
|
const separator = entry.indexOf(":");
|
|
@@ -47803,7 +48194,7 @@ const GLOB_METACHARACTERS = /[*?[]/;
|
|
|
47803
48194
|
/** Patterns named in the glob warning before the rest collapse to `(+N more)`. */
|
|
47804
48195
|
const MAX_LISTED_GLOB_PATTERNS = 10;
|
|
47805
48196
|
function importPoolToolList({ serverName, poolKey, value, warnOnGlobs }) {
|
|
47806
|
-
if (!isStringArray$
|
|
48197
|
+
if (!isStringArray$1(value)) {
|
|
47807
48198
|
warnWithFallback(void 0, `Ignored malformed value for ${poolKey} in Pool MCP server ${quoteValueForWarning(serverName)}: expected a list of strings`);
|
|
47808
48199
|
return;
|
|
47809
48200
|
}
|
|
@@ -48013,7 +48404,7 @@ var PoolMcp = class PoolMcp extends ToolMcp {
|
|
|
48013
48404
|
const poolAllow = isRecord$1(rawServer) ? rawServer.poolAllow : void 0;
|
|
48014
48405
|
return [serverName, {
|
|
48015
48406
|
...serverConfig,
|
|
48016
|
-
...isStringArray$
|
|
48407
|
+
...isStringArray$1(poolAllow) && { poolAllow }
|
|
48017
48408
|
}];
|
|
48018
48409
|
})), logger);
|
|
48019
48410
|
return new PoolMcp({
|
|
@@ -48244,7 +48635,7 @@ function pickServerLists(source) {
|
|
|
48244
48635
|
if (!isPlainObject$1(source)) return {};
|
|
48245
48636
|
return Object.fromEntries(QWENCODE_MCP_SERVER_LIST_KEYS.flatMap((key) => {
|
|
48246
48637
|
const value = source[key];
|
|
48247
|
-
return isStringArray$
|
|
48638
|
+
return isStringArray$1(value) ? [[key, value]] : [];
|
|
48248
48639
|
}));
|
|
48249
48640
|
}
|
|
48250
48641
|
/**
|
|
@@ -48937,7 +49328,7 @@ async function resolveRovodevMcpImportPath({ outputRoot, global, config, logger
|
|
|
48937
49328
|
}
|
|
48938
49329
|
function disabledNamesOf(config) {
|
|
48939
49330
|
const mcpBlock = config && isRecord$1(config.mcp) ? config.mcp : {};
|
|
48940
|
-
return isStringArray$
|
|
49331
|
+
return isStringArray$1(mcpBlock.disabledMcpServers) ? mcpBlock.disabledMcpServers : [];
|
|
48941
49332
|
}
|
|
48942
49333
|
/**
|
|
48943
49334
|
* The value `mcp.mcpConfigPath` needs so Rovo Dev reads the `mcp.json` rulesync
|
|
@@ -49353,7 +49744,7 @@ var RovodevMcp = class RovodevMcp extends ToolMcp {
|
|
|
49353
49744
|
return [];
|
|
49354
49745
|
}
|
|
49355
49746
|
const existingMcp = isRecord$1(existingParsed.mcp) ? { ...existingParsed.mcp } : {};
|
|
49356
|
-
const existingDisabled = isStringArray$
|
|
49747
|
+
const existingDisabled = isStringArray$1(existingMcp.disabledMcpServers) ? existingMcp.disabledMcpServers : [];
|
|
49357
49748
|
const managedNameSet = new Set(managedNames);
|
|
49358
49749
|
const reEnabled = existingDisabled.filter((name) => managedNameSet.has(name) && !disabledNames.includes(name));
|
|
49359
49750
|
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 +56903,9 @@ function convertClaudeToRulesyncPermissions(params) {
|
|
|
56512
56903
|
//#endregion
|
|
56513
56904
|
//#region src/features/permissions/cline-permissions.ts
|
|
56514
56905
|
/**
|
|
56515
|
-
* Cline CLI
|
|
56516
|
-
*
|
|
56906
|
+
* The legacy Cline CLI loaded command permissions from the `CLINE_COMMAND_PERMISSIONS`
|
|
56907
|
+
* environment variable. It supported only `allow` and `deny` (no `ask`), and only for
|
|
56908
|
+
* shell commands.
|
|
56517
56909
|
* Schema:
|
|
56518
56910
|
* ```json
|
|
56519
56911
|
* { "allow": ["pattern1", ...], "deny": ["pattern2", ...], "allowRedirects": false }
|
|
@@ -56521,7 +56913,14 @@ function convertClaudeToRulesyncPermissions(params) {
|
|
|
56521
56913
|
*
|
|
56522
56914
|
* Rulesync writes the JSON to `.cline/command-permissions.json` so users can do:
|
|
56523
56915
|
* `export CLINE_COMMAND_PERMISSIONS=$(cat .cline/command-permissions.json)`
|
|
56916
|
+
*
|
|
56917
|
+
* Deprecated: no Cline CLI 3 release reads `CLINE_COMMAND_PERMISSIONS`. Upstream removed
|
|
56918
|
+
* it from the docs in cline/cline@6354d209, and the CLI reference now says the CLI has no
|
|
56919
|
+
* allow or deny list for shell commands and recommends a `PreToolUse` hook instead
|
|
56920
|
+
* (https://docs.cline.bot/cli/cli-reference). The output is still generated for
|
|
56921
|
+
* compatibility, with a deprecation warning on every generation.
|
|
56524
56922
|
*/
|
|
56923
|
+
const CLINE_PERMISSIONS_DEPRECATION_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. Note that a hook returning `cancel: true` stops the whole Cline task, not just that one tool call (https://github.com/cline/cline/issues/14697). See https://docs.cline.bot/cli/cli-reference.";
|
|
56525
56924
|
const ClineCommandPermissionsSchema = z.looseObject({
|
|
56526
56925
|
allow: z.optional(z.array(z.string())),
|
|
56527
56926
|
deny: z.optional(z.array(z.string())),
|
|
@@ -56639,6 +57038,7 @@ var ClinePermissions = class ClinePermissions extends ToolPermissions {
|
|
|
56639
57038
|
});
|
|
56640
57039
|
}
|
|
56641
57040
|
static async fromRulesyncPermissions({ outputRoot = process.cwd(), rulesyncPermissions, global = false, logger }) {
|
|
57041
|
+
logger?.warn(CLINE_PERMISSIONS_DEPRECATION_WARNING);
|
|
56642
57042
|
const paths = ClinePermissions.getSettablePaths({ global });
|
|
56643
57043
|
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
56644
57044
|
const existingContent = await readFileContentOrNull(filePath) ?? "{}";
|
|
@@ -56668,7 +57068,7 @@ var ClinePermissions = class ClinePermissions extends ToolPermissions {
|
|
|
56668
57068
|
const mergedDeny = uniq([...existing.deny ?? [], ...dedupedDeny]).toSorted();
|
|
56669
57069
|
const denySet = new Set(mergedDeny);
|
|
56670
57070
|
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
|
|
57071
|
+
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
57072
|
const next = {
|
|
56673
57073
|
...existing,
|
|
56674
57074
|
allow: dedupedAllow,
|
|
@@ -59951,9 +60351,9 @@ function managedContinueToolNames(config) {
|
|
|
59951
60351
|
*/
|
|
59952
60352
|
function parseContinuePermissionLists(parsed) {
|
|
59953
60353
|
return {
|
|
59954
|
-
exclude: isStringArray$
|
|
59955
|
-
ask: isStringArray$
|
|
59956
|
-
allow: isStringArray$
|
|
60354
|
+
exclude: isStringArray$1(parsed.exclude) ? parsed.exclude : [],
|
|
60355
|
+
ask: isStringArray$1(parsed.ask) ? parsed.ask : [],
|
|
60356
|
+
allow: isStringArray$1(parsed.allow) ? parsed.allow : []
|
|
59957
60357
|
};
|
|
59958
60358
|
}
|
|
59959
60359
|
/**
|
|
@@ -60036,6 +60436,193 @@ const AUTO_APPROVE_KEYS = {
|
|
|
60036
60436
|
* @see https://code.visualstudio.com/docs/agents/reference/ai-settings
|
|
60037
60437
|
*/
|
|
60038
60438
|
const DEFAULT_PERMISSION_MODE_KEY = "chat.permissions.default";
|
|
60439
|
+
/**
|
|
60440
|
+
* The prefix every Agent Host sandbox setting shares. A `copilot.sandbox` leaf
|
|
60441
|
+
* at path `a.b` is written to `chat.agent.sandbox.a.b`.
|
|
60442
|
+
*/
|
|
60443
|
+
const SANDBOX_KEY_PREFIX = "chat.agent.sandbox.";
|
|
60444
|
+
/**
|
|
60445
|
+
* The `copilot.sandbox` leaves rulesync manages, each a flat VS Code setting
|
|
60446
|
+
* (`SANDBOX_KEY_PREFIX` + the dotted path) with the value shape VS Code
|
|
60447
|
+
* registers for it. The shape is re-checked on import, so a hand-written value
|
|
60448
|
+
* VS Code would not accept is skipped instead of failing the whole import.
|
|
60449
|
+
*
|
|
60450
|
+
* @see https://code.visualstudio.com/docs/agents/run/agent-sandboxing
|
|
60451
|
+
* @see https://code.visualstudio.com/docs/agents/reference/ai-settings
|
|
60452
|
+
*/
|
|
60453
|
+
const SANDBOX_LEAVES = [
|
|
60454
|
+
{
|
|
60455
|
+
path: ["enabled"],
|
|
60456
|
+
schema: CopilotSandboxEnabledSchema,
|
|
60457
|
+
normalize: (value) => value === true ? "on" : value === false ? "off" : value
|
|
60458
|
+
},
|
|
60459
|
+
{
|
|
60460
|
+
path: ["network", "allowNetwork"],
|
|
60461
|
+
schema: z.boolean()
|
|
60462
|
+
},
|
|
60463
|
+
{
|
|
60464
|
+
path: ["network", "allowLocalNetwork"],
|
|
60465
|
+
schema: z.boolean()
|
|
60466
|
+
},
|
|
60467
|
+
{
|
|
60468
|
+
path: ["allowUnsandboxedCommands"],
|
|
60469
|
+
schema: z.boolean()
|
|
60470
|
+
},
|
|
60471
|
+
{
|
|
60472
|
+
path: ["mcpServers"],
|
|
60473
|
+
schema: z.boolean()
|
|
60474
|
+
},
|
|
60475
|
+
{
|
|
60476
|
+
path: ["lspServers"],
|
|
60477
|
+
schema: z.boolean()
|
|
60478
|
+
},
|
|
60479
|
+
{
|
|
60480
|
+
path: ["credentials", "authenticategit"],
|
|
60481
|
+
schema: z.boolean()
|
|
60482
|
+
},
|
|
60483
|
+
{
|
|
60484
|
+
path: ["credentials", "authenticategh"],
|
|
60485
|
+
schema: z.boolean()
|
|
60486
|
+
},
|
|
60487
|
+
{
|
|
60488
|
+
path: ["fileSystem", "allowDevToolAccess"],
|
|
60489
|
+
schema: z.boolean()
|
|
60490
|
+
},
|
|
60491
|
+
{
|
|
60492
|
+
path: ["fileSystem", "userConfiguredPaths"],
|
|
60493
|
+
schema: CopilotSandboxUserConfiguredPathsSchema
|
|
60494
|
+
}
|
|
60495
|
+
];
|
|
60496
|
+
const USER_CONFIGURED_PATHS_KEY = `${SANDBOX_KEY_PREFIX}fileSystem.userConfiguredPaths`;
|
|
60497
|
+
/**
|
|
60498
|
+
* `copilot.sandbox` values that loosen the Agent Host sandbox. They are still
|
|
60499
|
+
* written — a sandbox policy is the user's call — but never silently. Each
|
|
60500
|
+
* `widens` names the value that does *not* loosen anything and reports the
|
|
60501
|
+
* rest, so an unexpected value is reported rather than ignored.
|
|
60502
|
+
*
|
|
60503
|
+
* @see https://code.visualstudio.com/docs/agents/run/agent-sandboxing
|
|
60504
|
+
*/
|
|
60505
|
+
const COPILOT_TRUST_AFFECTING_SANDBOX_PATHS = [
|
|
60506
|
+
{
|
|
60507
|
+
path: ["enabled"],
|
|
60508
|
+
reason: "turns the sandbox off, so agent terminal commands run unconfined",
|
|
60509
|
+
widens: (value) => value !== "on"
|
|
60510
|
+
},
|
|
60511
|
+
{
|
|
60512
|
+
path: ["network", "allowNetwork"],
|
|
60513
|
+
reason: "lets sandboxed commands reach the external network",
|
|
60514
|
+
widens: isNotFalse
|
|
60515
|
+
},
|
|
60516
|
+
{
|
|
60517
|
+
path: ["network", "allowLocalNetwork"],
|
|
60518
|
+
reason: "lets sandboxed commands reach local network resources",
|
|
60519
|
+
widens: isNotFalse
|
|
60520
|
+
},
|
|
60521
|
+
{
|
|
60522
|
+
path: ["allowUnsandboxedCommands"],
|
|
60523
|
+
reason: "lets the agent ask to run a blocked command outside the sandbox",
|
|
60524
|
+
widens: isNotFalse
|
|
60525
|
+
},
|
|
60526
|
+
{
|
|
60527
|
+
path: ["mcpServers"],
|
|
60528
|
+
reason: "runs locally launched MCP servers outside the sandbox",
|
|
60529
|
+
widens: isNotTrue
|
|
60530
|
+
},
|
|
60531
|
+
{
|
|
60532
|
+
path: ["lspServers"],
|
|
60533
|
+
reason: "runs language servers outside the sandbox",
|
|
60534
|
+
widens: isNotTrue
|
|
60535
|
+
},
|
|
60536
|
+
{
|
|
60537
|
+
path: ["credentials", "authenticategit"],
|
|
60538
|
+
reason: "hands your Git credentials to sandboxed commands",
|
|
60539
|
+
widens: isNotFalse
|
|
60540
|
+
},
|
|
60541
|
+
{
|
|
60542
|
+
path: ["credentials", "authenticategh"],
|
|
60543
|
+
reason: "hands your GitHub CLI authentication to sandboxed commands",
|
|
60544
|
+
widens: isNotFalse
|
|
60545
|
+
},
|
|
60546
|
+
{
|
|
60547
|
+
path: ["fileSystem", "allowDevToolAccess"],
|
|
60548
|
+
reason: "grants sandboxed commands developer-tool directories, which can hold credentials such as registry tokens",
|
|
60549
|
+
widens: isNotFalse
|
|
60550
|
+
},
|
|
60551
|
+
{
|
|
60552
|
+
path: [
|
|
60553
|
+
"fileSystem",
|
|
60554
|
+
"userConfiguredPaths",
|
|
60555
|
+
"readwritePaths"
|
|
60556
|
+
],
|
|
60557
|
+
reason: "adds paths sandboxed commands may write to, outside the working directory",
|
|
60558
|
+
widens: isNonEmptyList
|
|
60559
|
+
},
|
|
60560
|
+
{
|
|
60561
|
+
path: [
|
|
60562
|
+
"fileSystem",
|
|
60563
|
+
"userConfiguredPaths",
|
|
60564
|
+
"readonlyPaths"
|
|
60565
|
+
],
|
|
60566
|
+
reason: "adds paths sandboxed commands may read, outside the working directory",
|
|
60567
|
+
widens: isNonEmptyList
|
|
60568
|
+
}
|
|
60569
|
+
];
|
|
60570
|
+
const COPILOT_TOOL_LABEL = "GitHub Copilot";
|
|
60571
|
+
function sandboxKey(path) {
|
|
60572
|
+
return `${SANDBOX_KEY_PREFIX}${path.join(".")}`;
|
|
60573
|
+
}
|
|
60574
|
+
/**
|
|
60575
|
+
* Patch entries for every `copilot.sandbox` leaf the canonical config states.
|
|
60576
|
+
* Leaves it does not state are left as the file has them, so authoring only
|
|
60577
|
+
* `network.allowNetwork` never disturbs a hand-written `enabled`. The
|
|
60578
|
+
* `userConfiguredPaths` object merges per list for the same reason: authoring
|
|
60579
|
+
* `readwritePaths` keeps a hand-written `deniedPaths`, which would otherwise be
|
|
60580
|
+
* a restriction lost without a word.
|
|
60581
|
+
*/
|
|
60582
|
+
function buildSandboxPatch({ sandbox, existingSettings }) {
|
|
60583
|
+
const patch = {};
|
|
60584
|
+
for (const { path } of SANDBOX_LEAVES) {
|
|
60585
|
+
const value = readSandboxPath({
|
|
60586
|
+
sandbox,
|
|
60587
|
+
path
|
|
60588
|
+
});
|
|
60589
|
+
if (value === void 0 || value === UNREADABLE_SANDBOX_PATH) continue;
|
|
60590
|
+
const key = sandboxKey(path);
|
|
60591
|
+
if (key === USER_CONFIGURED_PATHS_KEY && isPlainObject$1(value)) {
|
|
60592
|
+
const existing = existingSettings[key];
|
|
60593
|
+
patch[key] = {
|
|
60594
|
+
...isPlainObject$1(existing) ? existing : {},
|
|
60595
|
+
...value
|
|
60596
|
+
};
|
|
60597
|
+
continue;
|
|
60598
|
+
}
|
|
60599
|
+
patch[key] = value;
|
|
60600
|
+
}
|
|
60601
|
+
return patch;
|
|
60602
|
+
}
|
|
60603
|
+
/**
|
|
60604
|
+
* Lift every managed `chat.agent.sandbox.*` key the file holds back into a
|
|
60605
|
+
* nested `copilot.sandbox` object. A value whose shape VS Code would reject is
|
|
60606
|
+
* skipped; the write path leaves it in place.
|
|
60607
|
+
*/
|
|
60608
|
+
function readSandboxFromSettings(settings) {
|
|
60609
|
+
const sandbox = {};
|
|
60610
|
+
for (const { path, schema, normalize } of SANDBOX_LEAVES) {
|
|
60611
|
+
const value = settings[sandboxKey(path)];
|
|
60612
|
+
const parsed = schema.safeParse(normalize === void 0 ? value : normalize(value));
|
|
60613
|
+
if (!parsed.success) continue;
|
|
60614
|
+
let cursor = sandbox;
|
|
60615
|
+
for (const segment of path.slice(0, -1)) {
|
|
60616
|
+
const next = cursor[segment];
|
|
60617
|
+
const child = isPlainObject$1(next) ? next : {};
|
|
60618
|
+
cursor[segment] = child;
|
|
60619
|
+
cursor = child;
|
|
60620
|
+
}
|
|
60621
|
+
const leaf = path[path.length - 1];
|
|
60622
|
+
if (leaf !== void 0) cursor[leaf] = parsed.data;
|
|
60623
|
+
}
|
|
60624
|
+
return Object.keys(sandbox).length > 0 ? sandbox : void 0;
|
|
60625
|
+
}
|
|
60039
60626
|
function isCopilotDefaultPermissionMode(value) {
|
|
60040
60627
|
return COPILOT_DEFAULT_PERMISSION_MODES.some((mode) => mode === value);
|
|
60041
60628
|
}
|
|
@@ -60080,7 +60667,9 @@ function buildAutoApproveValue(rules) {
|
|
|
60080
60667
|
* hand-written edits or urls map.
|
|
60081
60668
|
* The `copilot.defaultPermissionMode` override writes `chat.permissions.default`
|
|
60082
60669
|
* (the starting permission level of new chat sessions); without it the key is
|
|
60083
|
-
* left untouched.
|
|
60670
|
+
* left untouched. The `copilot.sandbox` override writes the Agent Host sandbox
|
|
60671
|
+
* settings (`chat.agent.sandbox.*`, see {@link SANDBOX_LEAVES}) per key, and
|
|
60672
|
+
* warns about every value it writes that loosens the sandbox.
|
|
60084
60673
|
* Only project scope is modeled: VS Code's user-scope settings.json lives at a
|
|
60085
60674
|
* platform-dependent path outside rulesync's home-relative global model.
|
|
60086
60675
|
*/
|
|
@@ -60128,6 +60717,26 @@ var CopilotPermissions = class CopilotPermissions extends ToolPermissions {
|
|
|
60128
60717
|
}
|
|
60129
60718
|
const defaultPermissionMode = config.copilot?.defaultPermissionMode;
|
|
60130
60719
|
if (defaultPermissionMode !== void 0) patch[DEFAULT_PERMISSION_MODE_KEY] = defaultPermissionMode;
|
|
60720
|
+
const sandbox = config.copilot?.sandbox;
|
|
60721
|
+
if (sandbox !== void 0) {
|
|
60722
|
+
Object.assign(patch, buildSandboxPatch({
|
|
60723
|
+
sandbox,
|
|
60724
|
+
existingSettings: parseSharedConfig({
|
|
60725
|
+
format: "jsonc",
|
|
60726
|
+
fileContent: existingContent,
|
|
60727
|
+
filePath
|
|
60728
|
+
})
|
|
60729
|
+
}));
|
|
60730
|
+
warnOnTrustAffectingEntries({
|
|
60731
|
+
toolLabel: COPILOT_TOOL_LABEL,
|
|
60732
|
+
entries: collectTrustAffectingSandboxPaths({
|
|
60733
|
+
sandbox,
|
|
60734
|
+
paths: COPILOT_TRUST_AFFECTING_SANDBOX_PATHS
|
|
60735
|
+
}),
|
|
60736
|
+
relativeFilePath: join(paths.relativeDirPath, paths.relativeFilePath),
|
|
60737
|
+
logger
|
|
60738
|
+
});
|
|
60739
|
+
}
|
|
60131
60740
|
return new CopilotPermissions({
|
|
60132
60741
|
outputRoot,
|
|
60133
60742
|
relativeDirPath: paths.relativeDirPath,
|
|
@@ -60164,9 +60773,14 @@ var CopilotPermissions = class CopilotPermissions extends ToolPermissions {
|
|
|
60164
60773
|
if (Object.keys(rules).length > 0) permission[category] = rules;
|
|
60165
60774
|
}
|
|
60166
60775
|
const defaultPermissionMode = settings[DEFAULT_PERMISSION_MODE_KEY];
|
|
60776
|
+
const sandbox = readSandboxFromSettings(settings);
|
|
60777
|
+
const copilot = {
|
|
60778
|
+
...isCopilotDefaultPermissionMode(defaultPermissionMode) && { defaultPermissionMode },
|
|
60779
|
+
...sandbox !== void 0 && { sandbox }
|
|
60780
|
+
};
|
|
60167
60781
|
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify({
|
|
60168
60782
|
permission,
|
|
60169
|
-
...
|
|
60783
|
+
...Object.keys(copilot).length > 0 && { copilot }
|
|
60170
60784
|
}, null, 2) });
|
|
60171
60785
|
}
|
|
60172
60786
|
validate() {
|
|
@@ -60480,8 +61094,8 @@ var CrushPermissions = class CrushPermissions extends ToolPermissions {
|
|
|
60480
61094
|
logger
|
|
60481
61095
|
});
|
|
60482
61096
|
const managedToolNames = managedCrushToolNames(config);
|
|
60483
|
-
const preservedAllowed = (isStringArray$
|
|
60484
|
-
const preservedDisabled = (isStringArray$
|
|
61097
|
+
const preservedAllowed = (isStringArray$1(existingPermissions?.["allowed_tools"]) ? existingPermissions[CRUSH_ALLOWED_TOOLS_KEY] : []).filter((entry) => entry.includes(":") || !managedToolNames.has(entry));
|
|
61098
|
+
const preservedDisabled = (isStringArray$1(existingOptions?.["disabled_tools"]) ? existingOptions[CRUSH_DISABLED_TOOLS_KEY] : []).filter((entry) => !managedToolNames.has(entry));
|
|
60485
61099
|
const allowedList = uniq$1([...preservedAllowed, ...generated.allowed]);
|
|
60486
61100
|
const disabledList = uniq$1([...preservedDisabled, ...generated.disabled]);
|
|
60487
61101
|
const patch = {};
|
|
@@ -60507,8 +61121,8 @@ var CrushPermissions = class CrushPermissions extends ToolPermissions {
|
|
|
60507
61121
|
const permissions = isRecord$1(this.json["permissions"]) ? this.json[CRUSH_PERMISSIONS_KEY] : {};
|
|
60508
61122
|
const options = isRecord$1(this.json["options"]) ? this.json[CRUSH_OPTIONS_KEY] : {};
|
|
60509
61123
|
const rulesyncConfig = convertCrushListsToRulesync({
|
|
60510
|
-
allowed: isStringArray$
|
|
60511
|
-
disabled: isStringArray$
|
|
61124
|
+
allowed: isStringArray$1(permissions["allowed_tools"]) ? permissions[CRUSH_ALLOWED_TOOLS_KEY] : [],
|
|
61125
|
+
disabled: isStringArray$1(options["disabled_tools"]) ? options[CRUSH_DISABLED_TOOLS_KEY] : []
|
|
60512
61126
|
});
|
|
60513
61127
|
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(rulesyncConfig, null, 2) });
|
|
60514
61128
|
}
|
|
@@ -62683,7 +63297,7 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
|
|
|
62683
63297
|
function convertGoosePermissionConfigToRulesync(userPermission) {
|
|
62684
63298
|
const permission = {};
|
|
62685
63299
|
for (const key of GOOSE_PERMISSION_LIST_KEYS) {
|
|
62686
|
-
const toolNames = isStringArray$
|
|
63300
|
+
const toolNames = isStringArray$1(userPermission[key]) ? userPermission[key] : [];
|
|
62687
63301
|
const action = GOOSE_LIST_TO_ACTION[key];
|
|
62688
63302
|
for (const toolName of toolNames) for (const category of GOOSE_TO_RULESYNC_TOOL_NAMES[toolName] ?? [toolName]) {
|
|
62689
63303
|
permission[category] ??= {};
|
|
@@ -63031,7 +63645,7 @@ const ACTION_RANK$3 = {
|
|
|
63031
63645
|
* (mirrors the Cursor adapter's preservation of unmanaged types).
|
|
63032
63646
|
*/
|
|
63033
63647
|
function unmanagedEntries(existingPermission, key) {
|
|
63034
|
-
return (isStringArray$
|
|
63648
|
+
return (isStringArray$1(existingPermission[key]) ? existingPermission[key] : []).filter((entry) => parseGrokEntry(entry) === null);
|
|
63035
63649
|
}
|
|
63036
63650
|
/**
|
|
63037
63651
|
* Bucket canonical rules into Grok's `[permission]` allow/deny/ask arrays of
|
|
@@ -63088,7 +63702,7 @@ function warnRemovedAllToolsRestrictions({ existingPermission, ranked, logger })
|
|
|
63088
63702
|
const generated = ranked.get(GROK_ALL_TOOLS_ENTRY);
|
|
63089
63703
|
for (const action of ["deny", "ask"]) {
|
|
63090
63704
|
const entries = existingPermission[action];
|
|
63091
|
-
if (!isStringArray$
|
|
63705
|
+
if (!isStringArray$1(entries)) continue;
|
|
63092
63706
|
if (!entries.some((entry) => parseGrokEntry(entry)?.category === "*")) continue;
|
|
63093
63707
|
if (generated !== void 0 && ACTION_RANK$3[generated] >= ACTION_RANK$3[action]) continue;
|
|
63094
63708
|
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 +63733,9 @@ function warnEntriesShadowedByAllTools({ ranked, logger }) {
|
|
|
63119
63733
|
* described more than once resolves to the strictest action.
|
|
63120
63734
|
*/
|
|
63121
63735
|
function parseGrokPermissionArrays(permission) {
|
|
63122
|
-
const allow = isStringArray$
|
|
63123
|
-
const deny = isStringArray$
|
|
63124
|
-
const ask = isStringArray$
|
|
63736
|
+
const allow = isStringArray$1(permission.allow) ? permission.allow : void 0;
|
|
63737
|
+
const deny = isStringArray$1(permission.deny) ? permission.deny : void 0;
|
|
63738
|
+
const ask = isStringArray$1(permission.ask) ? permission.ask : void 0;
|
|
63125
63739
|
const rules = Array.isArray(permission[GROKCLI_PERMISSION_RULES_KEY]) ? permission[GROKCLI_PERMISSION_RULES_KEY] : void 0;
|
|
63126
63740
|
if ((allow?.length ?? 0) + (deny?.length ?? 0) + (ask?.length ?? 0) + (rules?.length ?? 0) === 0) return null;
|
|
63127
63741
|
const result = {};
|
|
@@ -63329,20 +63943,20 @@ var HermesagentPermissions = class HermesagentPermissions extends ToolPermission
|
|
|
63329
63943
|
const permission = clonePermissionBlock(provenance.permission);
|
|
63330
63944
|
reconcileCommandAllowlist({
|
|
63331
63945
|
permission,
|
|
63332
|
-
commandAllowlist: isStringArray$
|
|
63946
|
+
commandAllowlist: isStringArray$1(config.command_allowlist) ? config.command_allowlist : []
|
|
63333
63947
|
});
|
|
63334
63948
|
const approvals = isRecord$1(config.approvals) ? config.approvals : {};
|
|
63335
63949
|
reconcileNativeDenies({
|
|
63336
63950
|
permission,
|
|
63337
63951
|
category: SHELL_PERMISSION_CATEGORY,
|
|
63338
|
-
patterns: isStringArray$
|
|
63952
|
+
patterns: isStringArray$1(approvals.deny) ? approvals.deny : []
|
|
63339
63953
|
});
|
|
63340
63954
|
const security = isRecord$1(config.security) ? config.security : {};
|
|
63341
63955
|
const websiteBlocklist = isRecord$1(security.website_blocklist) ? security.website_blocklist : {};
|
|
63342
63956
|
reconcileNativeDenies({
|
|
63343
63957
|
permission,
|
|
63344
63958
|
category: WEBFETCH_PERMISSION_CATEGORY,
|
|
63345
|
-
patterns: websiteBlocklist.enabled === true && isStringArray$
|
|
63959
|
+
patterns: websiteBlocklist.enabled === true && isStringArray$1(websiteBlocklist.domains) ? websiteBlocklist.domains : []
|
|
63346
63960
|
});
|
|
63347
63961
|
removeEmptyCategories(permission);
|
|
63348
63962
|
const { permission: _permission, hermes: _hermes, ...otherProvenance } = provenance;
|
|
@@ -64736,13 +65350,13 @@ function classifyExistingEntry({ emitted, capability, pattern, effect }) {
|
|
|
64736
65350
|
* how many entries rulesync does not generate.
|
|
64737
65351
|
*/
|
|
64738
65352
|
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$
|
|
65353
|
+
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
65354
|
rule,
|
|
64741
65355
|
unrelatedEntries: 0
|
|
64742
65356
|
};
|
|
64743
65357
|
const capability = rule.capability;
|
|
64744
65358
|
const isCatchAll = rule.match === void 0;
|
|
64745
|
-
const patterns = isStringArray$
|
|
65359
|
+
const patterns = isStringArray$1(rule.match) ? rule.match : [CATCH_ALL_PATTERN$6];
|
|
64746
65360
|
if (patterns.length === 0) return {
|
|
64747
65361
|
rule,
|
|
64748
65362
|
unrelatedEntries: 0
|
|
@@ -64926,7 +65540,7 @@ var KiroCapabilityPermissions = class KiroCapabilityPermissions extends ToolPerm
|
|
|
64926
65540
|
for (const rule of rules) {
|
|
64927
65541
|
if (!isImportableRule(rule) || !isRecord$1(rule) || !isPermissionAction$1(rule.effect)) continue;
|
|
64928
65542
|
const category = KIRO_CAPABILITY_TO_CANONICAL[rule.capability];
|
|
64929
|
-
const patterns = rule.match === void 0 ? [CATCH_ALL_PATTERN$6] : isStringArray$
|
|
65543
|
+
const patterns = rule.match === void 0 ? [CATCH_ALL_PATTERN$6] : isStringArray$1(rule.match) ? rule.match : [];
|
|
64930
65544
|
const categoryRules = permission[category] ??= {};
|
|
64931
65545
|
for (const pattern of patterns) {
|
|
64932
65546
|
const previous = categoryRules[pattern];
|
|
@@ -66644,8 +67258,8 @@ function importPoolTool({ toolConfig, bucket }) {
|
|
|
66644
67258
|
bucket[CATCH_ALL_PATTERN$3] = "deny";
|
|
66645
67259
|
return;
|
|
66646
67260
|
}
|
|
66647
|
-
const denied = isStringArray$
|
|
66648
|
-
const allowed = isStringArray$
|
|
67261
|
+
const denied = isStringArray$1(toolConfig[POOL_DENY_KEY]) ? toolConfig[POOL_DENY_KEY] : [];
|
|
67262
|
+
const allowed = isStringArray$1(toolConfig[POOL_ALLOW_KEY]) ? toolConfig[POOL_ALLOW_KEY] : [];
|
|
66649
67263
|
for (const pattern of denied.filter(isUsablePattern)) bucket[pattern] = "deny";
|
|
66650
67264
|
for (const pattern of allowed.filter(isUsablePattern)) bucket[pattern] ??= "allow";
|
|
66651
67265
|
}
|
|
@@ -68850,7 +69464,7 @@ function convertRovodevToolPermissionsToRulesync(toolPermissions) {
|
|
|
68850
69464
|
permission[category] ??= {};
|
|
68851
69465
|
permission[category][CATCH_ALL_PATTERN$1] = levels.reduce((strictest, level) => strictestAction(strictest, level ?? implicitLevel), permission[category][CATCH_ALL_PATTERN$1]);
|
|
68852
69466
|
}
|
|
68853
|
-
if (isStringArray$
|
|
69467
|
+
if (isStringArray$1(toolPermissions.allowedExternalPaths)) for (const path of toolPermissions.allowedExternalPaths) {
|
|
68854
69468
|
permission.read ??= {};
|
|
68855
69469
|
permission.read[path] ??= "allow";
|
|
68856
69470
|
}
|
|
@@ -69106,7 +69720,7 @@ function stripRefusedOverridePaths(override) {
|
|
|
69106
69720
|
*/
|
|
69107
69721
|
function collectTrustAffectingOverrideEntries({ tools, general }) {
|
|
69108
69722
|
const entries = [];
|
|
69109
|
-
const allowed = isStringArray$
|
|
69723
|
+
const allowed = isStringArray$1(tools[ALLOWED_KEY]) ? tools[ALLOWED_KEY] : [];
|
|
69110
69724
|
if (allowed.length > 0) entries.push({
|
|
69111
69725
|
label: `tools.allowed (${allowed.map(quoteValueForWarning).join(", ")})`,
|
|
69112
69726
|
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 +69871,7 @@ function withholdDeniedOverrideAllowed({ overrideVerbatimAllowed, exclude, logge
|
|
|
69257
69871
|
* hand-written entry went.
|
|
69258
69872
|
*/
|
|
69259
69873
|
function reportDroppedManagedEntries({ existingTools, allowedList, excludeList, logger }) {
|
|
69260
|
-
const dropped = (key, list) => uniq(isStringArray$
|
|
69874
|
+
const dropped = (key, list) => uniq(isStringArray$1(existingTools?.[key]) ? existingTools[key] : []).filter((entry) => {
|
|
69261
69875
|
if (list.includes(entry)) return false;
|
|
69262
69876
|
const toolName = parseTabnineEntry(entry)?.toolName;
|
|
69263
69877
|
return toolName === void 0 || !list.includes(toolName);
|
|
@@ -69339,7 +69953,7 @@ var TabninePermissions = class TabninePermissions extends ToolPermissions {
|
|
|
69339
69953
|
}
|
|
69340
69954
|
const overrideList = (key) => {
|
|
69341
69955
|
const value = overrideTools[key];
|
|
69342
|
-
if (value === void 0 || isStringArray$
|
|
69956
|
+
if (value === void 0 || isStringArray$1(value)) return value ?? [];
|
|
69343
69957
|
warnWithFallback(logger, `Tabnine CLI permissions: ignored tools.${key} of the tabnine override (${quoteValueForWarning(value)}); it must be a list of tool names.`);
|
|
69344
69958
|
return [];
|
|
69345
69959
|
};
|
|
@@ -69377,7 +69991,7 @@ var TabninePermissions = class TabninePermissions extends ToolPermissions {
|
|
|
69377
69991
|
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
69992
|
const existingTools = this.existingToolsGroup(existingContent);
|
|
69379
69993
|
const managedToolNames = new Set(Object.keys(config.permission).map((category) => category === "bash" || category === "*" ? SHELL_TOOL_NAME : toTabnineToolName(category)));
|
|
69380
|
-
const preservedEntries = (key) => (isStringArray$
|
|
69994
|
+
const preservedEntries = (key) => (isStringArray$1(existingTools?.[key]) ? existingTools[key] : []).filter((entry) => {
|
|
69381
69995
|
const toolName = parseTabnineEntry(entry)?.toolName;
|
|
69382
69996
|
return toolName === void 0 || !managedToolNames.has(toolName);
|
|
69383
69997
|
});
|
|
@@ -69455,7 +70069,7 @@ var TabninePermissions = class TabninePermissions extends ToolPermissions {
|
|
|
69455
70069
|
if (rules !== void 0 && !Object.hasOwn(rules, pattern)) rules[pattern] = action;
|
|
69456
70070
|
};
|
|
69457
70071
|
for (const [key, action] of [[EXCLUDE_KEY, "deny"], [ALLOWED_KEY, "allow"]]) {
|
|
69458
|
-
const entries = isStringArray$
|
|
70072
|
+
const entries = isStringArray$1(tools[key]) ? tools[key] : [];
|
|
69459
70073
|
for (const entry of entries) {
|
|
69460
70074
|
const parsed = parseTabnineEntry(entry);
|
|
69461
70075
|
if (parsed === void 0) {
|
|
@@ -70323,12 +70937,12 @@ const WIDE_CHARACTERS_PATTERN = /\p{Emoji_Presentation}|\p{Emoji_Modifier_Base}|
|
|
|
70323
70937
|
* The ranges are the `East_Asian_Width=A` property of Unicode 17.0, taken from
|
|
70324
70938
|
* the table `get-east-asian-width` 1.6.0 carries and merged where two are
|
|
70325
70939
|
* 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
|
|
70940
|
+
* that the one lookup adds no runtime dependency of its own, and the drift test
|
|
70941
|
+
* in `display-width.test.ts` is what keeps it honest: it loads the package,
|
|
70942
|
+
* pinned as a dev dependency, walks every code point, and fails when this
|
|
70943
|
+
* table and the property disagree in either direction. When that pin moves to
|
|
70944
|
+
* a newer Unicode, that test is what fails, and the table is regenerated from
|
|
70945
|
+
* the new property — its ranges, marks aside,
|
|
70332
70946
|
* merged where adjacent and written as escapes — with the version named here.
|
|
70333
70947
|
*
|
|
70334
70948
|
* Three stretches of the property are left out because they never reach this
|
|
@@ -72077,8 +72691,8 @@ var WarpPermissions = class WarpPermissions extends ToolPermissions {
|
|
|
72077
72691
|
const executionProfiles = isRecord$1(agents["execution_profiles"]) ? agents[EXECUTION_PROFILES_KEY] : void 0;
|
|
72078
72692
|
const defaultProfile = executionProfiles && isRecord$1(executionProfiles["default"]) ? executionProfiles[DEFAULT_PROFILE_KEY] : void 0;
|
|
72079
72693
|
const config = convertWarpToRulesyncPermissions({
|
|
72080
|
-
allow: defaultProfile ? isStringArray$
|
|
72081
|
-
deny: defaultProfile ? isStringArray$
|
|
72694
|
+
allow: defaultProfile ? isStringArray$1(defaultProfile["command_allowlist"]) ? defaultProfile[PROFILE_ALLOWLIST_KEY] : [] : isStringArray$1(profiles[ALLOWLIST_KEY]) ? profiles[ALLOWLIST_KEY] : [],
|
|
72695
|
+
deny: defaultProfile ? isStringArray$1(defaultProfile["command_denylist"]) ? defaultProfile[PROFILE_DENYLIST_KEY] : [] : isStringArray$1(profiles[DENYLIST_KEY]) ? profiles[DENYLIST_KEY] : []
|
|
72082
72696
|
});
|
|
72083
72697
|
const warpOverride = {};
|
|
72084
72698
|
for (const key of WARP_OVERRIDE_KEYS) if (profiles[key] !== void 0) warpOverride[key] = profiles[key];
|
|
@@ -72608,8 +73222,8 @@ var WarpcliPermissions = class WarpcliPermissions extends ToolPermissions {
|
|
|
72608
73222
|
const allowlist = defaultProfile[PROFILE_ALLOWLIST_KEY];
|
|
72609
73223
|
const denylist = defaultProfile[PROFILE_DENYLIST_KEY];
|
|
72610
73224
|
const config = convertWarpToRulesyncPermissions({
|
|
72611
|
-
allow: isStringArray$
|
|
72612
|
-
deny: isStringArray$
|
|
73225
|
+
allow: isStringArray$1(allowlist) ? allowlist : [],
|
|
73226
|
+
deny: isStringArray$1(denylist) ? denylist : []
|
|
72613
73227
|
});
|
|
72614
73228
|
const executionProfileOverride = liftExecutionProfileOverride(defaultProfile);
|
|
72615
73229
|
const warpcliOverride = {};
|
|
@@ -84624,7 +85238,7 @@ var BobSubagent = class extends RooSubagent {
|
|
|
84624
85238
|
const mode = super.toRooMode(rulesyncSubagent);
|
|
84625
85239
|
const frontmatter = rulesyncSubagent.getFrontmatter();
|
|
84626
85240
|
const bobSection = isRecord$1(frontmatter.bob) ? frontmatter.bob : {};
|
|
84627
|
-
if (isStringArray$
|
|
85241
|
+
if (isStringArray$1(bobSection.allowedSubagents)) mode.allowedSubagents = bobSection.allowedSubagents;
|
|
84628
85242
|
return mode;
|
|
84629
85243
|
}
|
|
84630
85244
|
};
|
|
@@ -90079,7 +90693,7 @@ var ZoocodeSubagent = class extends RooSubagent {
|
|
|
90079
90693
|
const mode = super.toRooMode(rulesyncSubagent);
|
|
90080
90694
|
const frontmatter = rulesyncSubagent.getFrontmatter();
|
|
90081
90695
|
const zoocodeSection = isRecord$1(frontmatter.zoocode) ? frontmatter.zoocode : {};
|
|
90082
|
-
if (isStringArray$
|
|
90696
|
+
if (isStringArray$1(zoocodeSection.allowedMcpServers)) mode.allowedMcpServers = zoocodeSection.allowedMcpServers;
|
|
90083
90697
|
return mode;
|
|
90084
90698
|
}
|
|
90085
90699
|
toRulesyncSubagents() {
|
|
@@ -90091,7 +90705,7 @@ var ZoocodeSubagent = class extends RooSubagent {
|
|
|
90091
90705
|
targets: ["zoocode"],
|
|
90092
90706
|
roo: restRooSection
|
|
90093
90707
|
};
|
|
90094
|
-
if (isStringArray$
|
|
90708
|
+
if (isStringArray$1(allowedMcpServers)) rebuilt.zoocode = { allowedMcpServers };
|
|
90095
90709
|
return new RulesyncSubagent({
|
|
90096
90710
|
outputRoot: ".",
|
|
90097
90711
|
frontmatter: rebuilt,
|
|
@@ -94032,7 +94646,8 @@ var NestedAgentsmdRule = class extends ToolRule {
|
|
|
94032
94646
|
* `<subprojectPath>/AGENTS.md` (project scope only); every other non-root rule
|
|
94033
94647
|
* folds into the root `AGENTS.md`, since Codex has no modular rules directory.
|
|
94034
94648
|
* The project files on the root-to-cwd chain share one `project_doc_max_bytes`
|
|
94035
|
-
* budget (32 KiB by default
|
|
94649
|
+
* budget (32 KiB by default, or the value set in the project's
|
|
94650
|
+
* `.codex/config.toml`), which the RulesProcessor warns about.
|
|
94036
94651
|
*
|
|
94037
94652
|
* @see https://learn.chatgpt.com/docs/agent-configuration/agents-md
|
|
94038
94653
|
*/
|
|
@@ -94044,6 +94659,29 @@ var CodexcliRule = class extends NestedAgentsmdRule {
|
|
|
94044
94659
|
};
|
|
94045
94660
|
}
|
|
94046
94661
|
};
|
|
94662
|
+
/**
|
|
94663
|
+
* Read the `project_doc_max_bytes` value set in the project's
|
|
94664
|
+
* `.codex/config.toml`, which Codex loads for trusted projects and which
|
|
94665
|
+
* overrides the 32 KiB default budget for project `AGENTS.md` files.
|
|
94666
|
+
*
|
|
94667
|
+
* Answers `undefined` when the file is missing, is not valid TOML, or does not
|
|
94668
|
+
* set the key to a positive integer, so the caller falls back to the default.
|
|
94669
|
+
*
|
|
94670
|
+
* @see https://learn.chatgpt.com/docs/config-file/config-reference
|
|
94671
|
+
*/
|
|
94672
|
+
async function readCodexcliProjectDocMaxBytes(outputRoot) {
|
|
94673
|
+
const content = await readFileContentOrNull(join(outputRoot, CODEXCLI_DIR, CODEXCLI_MCP_FILE_NAME));
|
|
94674
|
+
if (content === null) return;
|
|
94675
|
+
let parsed;
|
|
94676
|
+
try {
|
|
94677
|
+
parsed = smolToml.parse(content);
|
|
94678
|
+
} catch {
|
|
94679
|
+
return;
|
|
94680
|
+
}
|
|
94681
|
+
if (!isPlainObject$1(parsed)) return;
|
|
94682
|
+
const value = parsed.project_doc_max_bytes;
|
|
94683
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : void 0;
|
|
94684
|
+
}
|
|
94047
94685
|
//#endregion
|
|
94048
94686
|
//#region src/features/rules/commandcode-rule.ts
|
|
94049
94687
|
var CommandcodeRule = class CommandcodeRule extends ToolRule {
|
|
@@ -99418,7 +100056,10 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
99418
100056
|
collisionPolicy: "fold",
|
|
99419
100057
|
projectInstructionBudget: {
|
|
99420
100058
|
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` (
|
|
100059
|
+
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` (Codex reads that file only if the project is trusted).",
|
|
100060
|
+
readConfiguredBytes: readCodexcliProjectDocMaxBytes,
|
|
100061
|
+
configuredSource: "`project_doc_max_bytes` in `.codex/config.toml` (honored only if the project is trusted)",
|
|
100062
|
+
defaultLimitHint: "Rulesync does not read `~/.codex/config.toml`; ignore this warning if you already raised `project_doc_max_bytes` there."
|
|
99422
100063
|
}
|
|
99423
100064
|
}
|
|
99424
100065
|
}],
|
|
@@ -99963,7 +100604,7 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
99963
100604
|
factory
|
|
99964
100605
|
});
|
|
99965
100606
|
await this.warnForWarpLegacyRootFile(toolRules);
|
|
99966
|
-
this.warnForInstructionBudget({
|
|
100607
|
+
await this.warnForInstructionBudget({
|
|
99967
100608
|
toolRules,
|
|
99968
100609
|
meta
|
|
99969
100610
|
});
|
|
@@ -99984,14 +100625,20 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
99984
100625
|
* once, at its shallowest directory. With several output roots the warning
|
|
99985
100626
|
* names the root it refers to. An output root below the git root is measured
|
|
99986
100627
|
* without the parent directories' files Codex would also load, so the
|
|
99987
|
-
* estimate can come out low there. The budget checked is the
|
|
99988
|
-
*
|
|
100628
|
+
* estimate can come out low there. The budget checked is the value set in the
|
|
100629
|
+
* output root's tool config when the tool supports one (Codex's
|
|
100630
|
+
* `project_doc_max_bytes` in `.codex/config.toml`), else the tool's default;
|
|
100631
|
+
* the warning names which one it used. Global scope is not
|
|
99989
100632
|
* checked because Codex loads the personal `~/.codex/AGENTS.md` outside the
|
|
99990
100633
|
* project budget.
|
|
99991
100634
|
*/
|
|
99992
|
-
warnForInstructionBudget({ toolRules, meta }) {
|
|
100635
|
+
async warnForInstructionBudget({ toolRules, meta }) {
|
|
99993
100636
|
const budget = meta.projectInstructionBudget;
|
|
99994
100637
|
if (budget === void 0 || this.global) return;
|
|
100638
|
+
const configuredBytes = await budget.readConfiguredBytes?.(this.outputRoot);
|
|
100639
|
+
const limitBytes = configuredBytes ?? budget.bytes;
|
|
100640
|
+
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"}`;
|
|
100641
|
+
const remedy = configuredBytes === void 0 && budget.defaultLimitHint !== void 0 ? `${budget.remedy} ${budget.defaultLimitHint}` : budget.remedy;
|
|
99995
100642
|
const files = toolRules.map((rule) => ({
|
|
99996
100643
|
dir: normalizeRelativeDir(rule.getRelativeDirPath()),
|
|
99997
100644
|
path: toPosixPath(join(rule.getRelativeDirPath(), rule.getRelativeFilePath())),
|
|
@@ -100001,8 +100648,8 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
100001
100648
|
const location = this.outputRoot === process.cwd() ? "" : ` under '${this.outputRoot}'`;
|
|
100002
100649
|
const rootFiles = files.filter(({ dir }) => dir === ".");
|
|
100003
100650
|
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 ${
|
|
100651
|
+
if (rootBytes > limitBytes) {
|
|
100652
|
+
this.logger.warn(`The generated ${this.toolTarget} root instruction file${location} (${rootFiles.map(({ path }) => path).join(", ")}) is ${rootBytes} bytes, over the ${limitLabel}. ${remedy}`);
|
|
100006
100653
|
return;
|
|
100007
100654
|
}
|
|
100008
100655
|
const reportedDirs = [];
|
|
@@ -100011,9 +100658,9 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
100011
100658
|
if (reportedDirs.some((dir) => isAncestorOrSelf(dir, file.dir))) continue;
|
|
100012
100659
|
const chain = files.filter(({ dir }) => isAncestorOrSelf(dir, file.dir));
|
|
100013
100660
|
const chainBytes = chain.reduce((sum, { bytes }) => sum + bytes, 0);
|
|
100014
|
-
if (chainBytes >
|
|
100661
|
+
if (chainBytes > limitBytes) {
|
|
100015
100662
|
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 ${
|
|
100663
|
+
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). ${remedy}`);
|
|
100017
100664
|
}
|
|
100018
100665
|
}
|
|
100019
100666
|
}
|
|
@@ -103918,4 +104565,4 @@ async function importChecksCore(params) {
|
|
|
103918
104565
|
//#endregion
|
|
103919
104566
|
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
104567
|
|
|
103921
|
-
//# sourceMappingURL=import-
|
|
104568
|
+
//# sourceMappingURL=import-BsEFYJsg.js.map
|